生活随笔
收集整理的這篇文章主要介紹了
多进程与多线程(1)
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
?說到多進程和多線程,我就想起了《The art of Multiprocessor Programming》中的第一章的例子(我就看了第一章)
?
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?
我對多線程編程不是很熟,所以想用這個例子學(xué)習(xí)一下多線程編程。
單線程編程
如果不會用線程,那么就用最簡單的單線程解決這個問題。
//example1.c #include?<stdio.h> ?#include?<stdlib.h> ?#include?<math.h> ?long ?long ?isPrime(int ?num)?{? ????long ?long ?i,?s;? ????s?=?(long ?long )sqrt(double (num));? ????for (i?=?2;?i?<?s;?i++)? ????????if (num?%?i?==?0)? ????????????return ?0;? ????return ?1;? }? int ?main()?{? ????long ?long ?i,primes_count?=?0;? ????for (i?=?1?;?i?<=?10000000000L;?i++)? ????????primes_count?+=??isPrime(i);? ????printf("%lld?primes);? ????return ?0;? }? 注意:編譯時加上-lm,gcc -lm example1.c ?; 打印long long整數(shù)要用%lld
如果真用這段代碼跑10000000000L,估計幾個小時跑不完,幾天不知道能跑完嗎。我打算在16核的機器上跑一下,不過16核和單核一樣,因為是單線程。
?
多線程方法1
如果會使用線程,一個簡單的方法是10個線程平分這些數(shù)。
-------------------------------------------------------------------------------------------
pthread相關(guān)數(shù)據(jù)結(jié)構(gòu)和函數(shù)
pthread_t 調(diào)用pthread_t返回的線程號,就是一個unsigned int;?
int pthread_create(pthread_t *, pthread_attr_t *, ?void* ?(*) (void*) , void*);
第一個參數(shù)是一個pthread_t,第二個是pthread_attr_t,具體看下面的用法,第三個是線程要運行的主函數(shù),第四個是要給這個函數(shù)的參數(shù)。
(void*) (*) (void*) 是一個函數(shù)類型,就是類似于 void * ?run (void*)的函數(shù)。
void pthread_exit() 線程退出
int pthread_join(pthread_t *, void **) 主線程等待子線程退出,第二個參數(shù)直接用NULL就行
----------------------------------------------------------------------------------------------------------
//example2.c #include?<stdio.h> ?#include?<stdlib.h> ?#include?<pthread.h> ?#include?<sys/types.h> ?#include?<unistd.h> ?#include?<math.h> ?long ?long ?isPrime(long ?long ?num)?{? ????long ?long ?i,?s;? ????s?=?(long ?long )sqrt((double )num);? ????for (i?=?2;?i?<?s;?i++)? ????????if (num?%?i?==?0)? ????????????return ?0;? ????return ?1;? }? ? void ?*?run(void ?*?param)?{? ????long ?long ?start,end;? ????long ?long ?i,primes_count?=?0;? ????start?=(?(long ?long *)param)[0];? ????end??=(?(long ?long *)param)[1];? ????for (i?=?start?;?i?<?end;?i++)? ????????primes_count?+=??isPrime(i);? ????printf("Process?%d?Thread?%u?gets?%lld?primes?from?%lld?to?%lld\n" ,getpid(),(unsigned)pthread_self(),primes_count,start,end);? ????pthread_exit(0);? }? ? int ?main()?{? ????pthread_t?tid[10];? ????pthread_attr_t?attr;? ????long ?long ?start_end[10][2];? ????long ?long ??i;? ????for (i?=?0;?i?<?10;?i++)? ????{? ????????start_end[i][0]?=?i*10000;? ????????start_end[i][1]?=?(i+1)*10000;?? ????????pthread_attr_init(&attr);? ????????pthread_create(&tid[i],&attr,run,(void *)start_end[i]);? ????}? ????for (i=?0;?i?<?10;?i++)? ????????pthread_join(tid[i],NULL);? ????return ?0;? }? 注意:使用pthread編譯要加上-lpthread。 gcc -lm -lpthread example2.c
運行結(jié)果:(不是10^10,只計算到100000)
------------------------------------------------------------------------------------------------
遇到的問題:
最開始我的主函數(shù)寫成這樣:
int ?main()?{? ????pthread_t?tid[10];? ????pthread_attr_t?attr;? ????long ?long ?start_end[2];? ????long ?long ?i;? ????for (i?=?0;?i?<?10;?i++)? ????{? ????????start_end[0]?=?i*10000;? ????????start_end[1]?=?(i+1)*10000;? ????????pthread_attr_init(&attr);? ????????pthread_create(&tid[i],&attr,run,(void *)start_end);? ????????pthread_join(tid[i],NULL);? ????}? ????return ?0;? }? 很明顯pthread_join地方寫錯了,這樣第一個進程運行完了才會創(chuàng)建第二個,但是由于每個線程平分,所以我沒發(fā)現(xiàn)結(jié)果有異常。
寫第三個程序時pthread_join也寫錯了位置,這才發(fā)現(xiàn)。但是我將pthread_join放到后面時,發(fā)現(xiàn)每個線程處理的數(shù)據(jù)段都是90000-100000,沒找到原因,因此我的start_end改成了二維數(shù)組,這樣各個線程就不會干擾了,具體的原因以后再研究。
--------------------------------------------------------------------------------------
這個程序應(yīng)該比第一個快,但是判斷第一段數(shù)據(jù)的線程使用的時間肯定沒有最后一段使用的時間的千分之一,最后9個人看一個人干活,我突然想到了什么。。。
多線程方法2
多線程的目的就是壓榨計算資源。我們可以使用下面的方法。
每個線程取一個數(shù),然后判斷,判斷完了繼續(xù)取下一個,讓每個線程都不停的干活
線程取了1,判斷,線程2取2判斷,線程3取3,這時線程1干完了,接著取4,線程2又取5。。。。
#include?<stdio.h> ?#include?<stdlib.h> ?#include?<pthread.h> ?#include?<sys/types.h> ?#include?<unistd.h> ?#include?<math.h> ?long ?long ?number?=?1;?//每個線程先取數(shù),再把這個數(shù)加1long ?long ?isPrime(long ?long ?num)?{? ????long ?long ?i,?s;? ????s?=?(long ?long )sqrt((double )num);? ????for (i?=?2;?i?<?s;?i++)? ????????if (num?%?i?==?0)? ????????????return ?0;? ????return ?1;? }? static ?pthread_mutex_t?mutex;?void ?*?run(void ?*?param)?{? ????long ?long ?primes_count=0;? ????long ?long ?i?=?1;? ????while (i?<?100000)? ????{? //要使用鎖 ????????pthread_mutex_lock(&mutex);? ????????i?=?number;? ????????number?++;? ????????pthread_mutex_unlock(&mutex);? ????????primes_count?+=??isPrime(i);? ????}? ????printf("Thread?%u?gets?%lld?primes\n" ,(unsigned??int )pthread_self(),primes_count);? ????pthread_exit(0);? }? ? ?int ?main()? {? ????pthread_t?tid[10];? ????pthread_attr_t?attr;? ????pthread_mutex_init(&mutex,NULL);? ????int ?i;? ? ????for (i?=?0;?i?<?10;?i++)? ????{? ????????pthread_attr_init(&attr);? ????????pthread_attr_setscope?(&attr,?PTHREAD_SCOPE_SYSTEM);? ????????pthread_create(&tid[i],&attr,run,NULL);? ?????????一開始把join放這,結(jié)果只有第一個線程干活,找了好久才發(fā)現(xiàn)位置錯了 ????}? ????for (i?=?0;?i?<?10;?i++)? ????????pthread_join(tid[i],NULL);? ????return ?0;? }? 運行結(jié)果:
多進程實現(xiàn)
用多線程能完成的,多進程也可以。
#include?<stdio.h> ?#include?<stdlib.h> ?#include?<pthread.h> ?#include?<sys/types.h> ?#include?<unistd.h> ?#include?<math.h> ?#include?<sys/ipc.h> ?#include?<sys/shm.h> ?#include?<errno.h> ?#include?<string.h> ?long ?long ?isPrime(long ?long ?num)?{? ????long ?long ?i,?s;? ????s?=?(long ?long )sqrt((double )num);? ????for (i?=?2;?i?<?s;?i++)? ????????if (num?%?i?==?0)? ????????????return ?0;? ????return ?1;? }? ? int ?main()?{? ????pid_t?pid[10];? ????int ?shmid;? ????long ?long ??i,primes_count=0;? ????long ?long ?*?addr;? ????shmid?=?shmget(IPC_PRIVATE,4096,IPC_CREAT|0600);? ????if (shmid?<?0)? ????{? ????????printf("get?shared?memory?failed\n" );? ????????exit(1);? ????}? ????addr?=?(long ?long *)?shmat(shmid,0,0);? ????*addr?=?1;? ????for (i?=?0;?i?<?3;?i++)? ????????fork();? ????while (i?<?100000)? ????{? ????????i?=?(*addr);? ????????(*addr)?++;? ????????primes_count?+=?isPrime(i);? ????}? ????printf("pid?%d?get?%lld?primes\n" ,getpid(),primes_count);? ????return ?0;? }? ?
注意:(1)使用循環(huán)fork,那么子進程還會fork,因此fork三次就得到了8個進程,在fork就16個進程了。要想產(chǎn)生10個進程,可以在主進程里寫10個fork語句,還得寫很多if(fork()==0)語句,懶得寫,就直接用循環(huán)了。加上主進程一個8個,先生完了孩子再干活,生完之后都成了兄弟,一起干。
? ? ? ? ? ?(2)這個程序有個嚴重的問題,使用了共享內(nèi)存,但是沒有使用同步機制,沒有鎖,挺麻煩的,不會用,就沒寫。結(jié)果可能不正確。
? 運行結(jié)果:
?小結(jié)
我就截幾個圖
這是單線程的,CPU使用了100%,看來也不錯,但是我的CPU有200%,有兩個核(我用的虛擬機,可以分16個核哦親),因此單線程不能充分利用計算資源。
多線程的把我的CPU都用完了。哈哈
多進程的呢?算一算,也是200%。
其實在Linux里面線程也是進程,稱為LWP(Light Weighted Process),只不過在生成的時候參數(shù)不同。在以后的文章里會深入研究。
?
轉(zhuǎn)載于:https://blog.51cto.com/nxlhero/1075979
總結(jié)
以上是生活随笔 為你收集整理的多进程与多线程(1) 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。