多进程与多线程(1)

简介:

 

On the first day of your new job, your boss asks you to find all primes between 1 and 10^10(never mind why), using a parallel machine that supports ten concurrent threads. This machine is rented by the minute, so the longer your program takes, the more it costs. You want to make a good impression. What do you do?

我对多线程编程不是很熟,所以想用这个例子学习一下多线程编程。

单线程编程

如果不会用线程,那么就用最简单的单线程解决这个问题。


 
 
  1. //example1.c
  2. #include <stdio.h> 
  3. #include <stdlib.h> 
  4. #include <math.h> 
  5. long long isPrime(int num) 
  6.     long long i, s; 
  7.     s = (long long)sqrt(double(num)); 
  8.     for(i = 2; i < s; i++) 
  9.         if(num % i == 0) 
  10.             return 0; 
  11.     return 1; 
  12. int main() 
  13.     long long i,primes_count = 0; 
  14.     for(i = 1 ; i <= 10000000000L; i++) 
  15.         primes_count +=  isPrime(i); 
  16.     printf("%lld primes); 
  17.     return 0; 

注意:编译时加上-lm,gcc -lm example1.c  ; 打印long long整数要用%lld

如果真用这段代码跑10000000000L,估计几个小时跑不完,几天不知道能跑完吗。我打算在16核的机器上跑一下,不过16核和单核一样,因为是单线程。

 

多线程方法1

如果会使用线程,一个简单的方法是10个线程平分这些数。

-------------------------------------------------------------------------------------------

pthread相关数据结构和函数

pthread_t 调用pthread_t返回的线程号,就是一个unsigned int; 

int pthread_create(pthread_t *, pthread_attr_t *,  void*  (*) (void*) , void*);

第一个参数是一个pthread_t,第二个是pthread_attr_t,具体看下面的用法,第三个是线程要运行的主函数,第四个是要给这个函数的参数。

(void*) (*) (void*) 是一个函数类型,就是类似于 void *  run (void*)的函数。

void pthread_exit() 线程退出

int pthread_join(pthread_t *, void **) 主线程等待子线程退出,第二个参数直接用NULL就行

----------------------------------------------------------------------------------------------------------


 
 
  1. //example2.c
  2. #include <stdio.h> 
  3. #include <stdlib.h> 
  4. #include <pthread.h> 
  5. #include <sys/types.h> 
  6. #include <unistd.h> 
  7. #include <math.h> 
  8. long long isPrime(long long num) 
  9.     long long i, s; 
  10.     s = (long long)sqrt((double)num); 
  11.     for(i = 2; i < s; i++) 
  12.         if(num % i == 0) 
  13.             return 0; 
  14.     return 1; 
  15.  
  16. void * run(void * param) 
  17.     long long start,end; 
  18.     long long i,primes_count = 0; 
  19.     start =( (long long*)param)[0]; 
  20.     end  =( (long long*)param)[1]; 
  21.     for(i = start ; i < end; i++) 
  22.         primes_count +=  isPrime(i); 
  23.     printf("Process %d Thread %u gets %lld primes from %lld to %lld\n",getpid(),(unsigned)pthread_self(),primes_count,start,end); 
  24.     pthread_exit(0); 
  25.  
  26. int main() 
  27.     pthread_t tid[10]; 
  28.     pthread_attr_t attr; 
  29.     long long start_end[10][2]; 
  30.     long long  i; 
  31.     for(i = 0; i < 10; i++) 
  32.     { 
  33.         start_end[i][0] = i*10000; 
  34.         start_end[i][1] = (i+1)*10000;  
  35.         pthread_attr_init(&attr); 
  36.         pthread_create(&tid[i],&attr,run,(void*)start_end[i]); 
  37.     } 
  38.     for(i= 0; i < 10; i++) 
  39.         pthread_join(tid[i],NULL); 
  40.     return 0; 

注意:使用pthread编译要加上-lpthread。 gcc -lm -lpthread example2.c

运行结果:(不是10^10,只计算到100000)

------------------------------------------------------------------------------------------------

遇到的问题:

最开始我的主函数写成这样:


 
 
  1. int main() 
  2.     pthread_t tid[10]; 
  3.     pthread_attr_t attr; 
  4.     long long start_end[2]; 
  5.     long long i; 
  6.     for(i = 0; i < 10; i++) 
  7.     { 
  8.         start_end[0] = i*10000; 
  9.         start_end[1] = (i+1)*10000; 
  10.         pthread_attr_init(&attr); 
  11.         pthread_create(&tid[i],&attr,run,(void*)start_end); 
  12.         pthread_join(tid[i],NULL); 
  13.     } 
  14.     return 0; 

很明显pthread_join地方写错了,这样第一个进程运行完了才会创建第二个,但是由于每个线程平分,所以我没发现结果有异常。

写第三个程序时pthread_join也写错了位置,这才发现。但是我将pthread_join放到后面时,发现每个线程处理的数据段都是90000-100000,没找到原因,因此我的start_end改成了二维数组,这样各个线程就不会干扰了,具体的原因以后再研究。

--------------------------------------------------------------------------------------

这个程序应该比第一个快,但是判断第一段数据的线程使用的时间肯定没有最后一段使用的时间的千分之一,最后9个人看一个人干活,我突然想到了什么。。。

多线程方法2

多线程的目的就是压榨计算资源。我们可以使用下面的方法。

每个线程取一个数,然后判断,判断完了继续取下一个,让每个线程都不停的干活

线程取了1,判断,线程2取2判断,线程3取3,这时线程1干完了,接着取4,线程2又取5。。。。


 
 
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <pthread.h> 
  4. #include <sys/types.h> 
  5. #include <unistd.h> 
  6. #include <math.h> 
  7. long long number = 1; //每个线程先取数,再把这个数加1
  8. long long isPrime(long long num) 
  9.     long long i, s; 
  10.     s = (long long)sqrt((double)num); 
  11.     for(i = 2; i < s; i++) 
  12.         if(num % i == 0) 
  13.             return 0; 
  14.     return 1; 
  15. static pthread_mutex_t mutex; 
  16. void * run(void * param) 
  17.     long long primes_count=0; 
  18.     long long i = 1; 
  19.     while(i < 100000) 
  20.     { 
  21. //要使用锁
  22.         pthread_mutex_lock(&mutex); 
  23.         i = number; 
  24.         number ++; 
  25.         pthread_mutex_unlock(&mutex); 
  26.         primes_count +=  isPrime(i); 
  27.     } 
  28.     printf("Thread %u gets %lld primes\n",(unsigned  int)pthread_self(),primes_count); 
  29.     pthread_exit(0); 
  30.  
  31.  int main() 
  32.     pthread_t tid[10]; 
  33.     pthread_attr_t attr; 
  34.     pthread_mutex_init(&mutex,NULL); 
  35.     int i; 
  36.  
  37.     for(i = 0; i < 10; i++) 
  38.     { 
  39.         pthread_attr_init(&attr); 
  40.         pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); 
  41.         pthread_create(&tid[i],&attr,run,NULL); 
  42.         //pthread_join(tid[i],NULL); 一开始把join放这,结果只有第一个线程干活,找了好久才发现位置错了
  43.     } 
  44.     for(i = 0; i < 10; i++) 
  45.         pthread_join(tid[i],NULL); 
  46.     return 0; 

运行结果:

多进程实现

用多线程能完成的,多进程也可以。


   
   
  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <pthread.h> 
  4. #include <sys/types.h> 
  5. #include <unistd.h> 
  6. #include <math.h> 
  7. #include <sys/ipc.h> 
  8. #include <sys/shm.h> 
  9. #include <errno.h> 
  10. #include <string.h> 
  11. long long isPrime(long long num) 
  12.     long long i, s; 
  13.     s = (long long)sqrt((double)num); 
  14.     for(i = 2; i < s; i++) 
  15.         if(num % i == 0) 
  16.             return 0; 
  17.     return 1; 
  18.  
  19. int main() 
  20.     pid_t pid[10]; 
  21.     int shmid; 
  22.     long long  i,primes_count=0; 
  23.     long long * addr; 
  24.     shmid = shmget(IPC_PRIVATE,4096,IPC_CREAT|0600); 
  25.     if(shmid < 0) 
  26.     { 
  27.         printf("get shared memory failed\n"); 
  28.         exit(1); 
  29.     } 
  30.     addr = (long long*) shmat(shmid,0,0); 
  31.     *addr = 1; 
  32.     for(i = 0; i < 3; i++) 
  33.         fork(); 
  34.     while(i < 100000) 
  35.     { 
  36.         i = (*addr); 
  37.         (*addr) ++; 
  38.         primes_count += isPrime(i); 
  39.     } 
  40.     printf("pid %d get %lld primes\n",getpid(),primes_count); 
  41.     return 0; 

 

注意:(1)使用循环fork,那么子进程还会fork,因此fork三次就得到了8个进程,在fork就16个进程了。要想产生10个进程,可以在主进程里写10个fork语句,还得写很多if(fork()==0)语句,懒得写,就直接用循环了。加上主进程一个8个,先生完了孩子再干活,生完之后都成了兄弟,一起干。

           (2)这个程序有个严重的问题,使用了共享内存,但是没有使用同步机制,没有锁,挺麻烦的,不会用,就没写。结果可能不正确

  运行结果:

 小结

我就截几个图

这是单线程的,CPU使用了100%,看来也不错,但是我的CPU有200%,有两个核(我用的虚拟机,可以分16个核哦亲),因此单线程不能充分利用计算资源。

多线程的把我的CPU都用完了。哈哈

多进程的呢?算一算,也是200%。

其实在Linux里面线程也是进程,称为LWP(Light Weighted Process),只不过在生成的时候参数不同。在以后的文章里会深入研究。



本文转自nxlhero 51CTO博客,原文链接:http://blog.51cto.com/nxlhero/1075979,如需转载请自行联系原作者

相关文章
|
存储 Linux API
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
在计算机系统的底层架构中,操作系统肩负着资源管理与任务调度的重任。当我们启动各类应用程序时,其背后复杂的运作机制便悄然展开。程序,作为静态的指令集合,如何在系统中实现动态执行?本文带你一探究竟!
【Linux进程概念】—— 操作系统中的“生命体”,计算机里的“多线程”
|
7月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
1063 2
|
7月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
374 1
|
7月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
351 1
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
并行计算 Linux
Linux内核中的线程和进程实现详解
了解进程和线程如何工作,可以帮助我们更好地编写程序,充分利用多核CPU,实现并行计算,提高系统的响应速度和计算效能。记住,适当平衡进程和线程的使用,既要拥有独立空间的'兄弟',也需要在'家庭'中分享和并行的成员。对于这个世界,现在,你应该有一个全新的认识。
427 67
|
11月前
|
调度 开发工具 Android开发
【HarmonyOS Next】鸿蒙应用进程和线程详解
进程的定义: 进程是系统进行资源分配的基本单位,是操作系统结构的基础。 在鸿蒙系统中,一个应用下会有三类进程:
409 0
|
SQL 监控 网络协议
YashanDB进程线程体系
YashanDB进程线程体系
|
消息中间件 调度
如何区分进程、线程和协程?看这篇就够了!
本课程主要探讨操作系统中的进程、线程和协程的区别。进程是资源分配的基本单位,具有独立性和隔离性;线程是CPU调度的基本单位,轻量且共享资源,适合并发执行;协程更轻量,由程序自身调度,适合I/O密集型任务。通过学习这些概念,可以更好地理解和应用它们,以实现最优的性能和资源利用。
491 11
|
Java Linux 调度
硬核揭秘:线程与进程的底层原理,面试高分必备!
嘿,大家好!我是小米,29岁的技术爱好者。今天来聊聊线程和进程的区别。进程是操作系统中运行的程序实例,有独立内存空间;线程是进程内的最小执行单元,共享内存。创建进程开销大但更安全,线程轻量高效但易引发数据竞争。面试时可强调:进程是资源分配单位,线程是CPU调度单位。根据不同场景选择合适的并发模型,如高并发用线程池。希望这篇文章能帮你更好地理解并回答面试中的相关问题,祝你早日拿下心仪的offer!
483 6

热门文章

最新文章