閱讀389 返回首頁    go 阿裏雲 go 技術社區[雲棲]


pthread vs openMP之我見

前兩天看了些並行計算的文章,了解了一些並行計算的方法和原理。然後發現多線程實現裏麵還有個openMP,這個以前從來沒見過(火星了),之前隻是知道pthread線程庫和微軟也實現了一套線程。又看了看openMP的一些教程才知道它是怎麼回事。

  pthread全稱應該是POSIX THREAD,顧名思義這個肯定是按照POSIX對線程的標準而設計的。目前我所知道的有兩個版本:Linux Thread(較早)和NPTL(主流?)。pthread庫是一套關於線程的API,提供“遵循”(各平台實現各異)POSIX標準的線程相關的功能。

  openMP不同於pthread的地方是,它是根植於編譯器的(也要包含頭文件omp.h),而不是在各係統平台是做文章。它貌似更偏向於將原來串行化的程序,通過加入一些適當的編譯器指令(compiler directive)變成並行執行,從而提高代碼運行的速率。如:

複製代碼
 1 #include<omp.h>
 2 #include<stdio.h>
 3 
 4  #define ARRAY_SIZE 1000
 5  #define CHUNK_SIZE 100
 6 
 7  int main()
 8 {
 9     int array[ARRAY_SIZE];
10     int thread_num = ARRAY_SIZE/CHUNK_SIZE+1;
11     omp_set_num_threads(thread_num);
12 
13     //init array
14      int i;
15     for(i=0;i<ARRAY_SIZE;i++)
16     {
17         array[i]=i;
18     }
19 
20  #pragma omp parallel for schedule(guided,CHUNK_SIZE) private(i)
21     for(i=0;i<ARRAY_SIZE;i++)
22     {
23         int n = array[i];
24         int num_of_one=0;
25         if(n!=0)
26         {
27             num_of_one++;
28             while((n=n&(n-1))!=0)
29             {
30                 num_of_one++;
31             }
32         }
33         array[i]=num_of_one;
34     
35     }
36     for(i=0;i<ARRAY_SIZE;i++)
37     {
38         printf("%d ",array[i]);
39     }
40     printf("\n");
41     return 0;
42 
43 }
44 
45  
複製代碼

  上麵一段代碼是通過加了一條函數調用(11行)和一條編譯器指令(20行),從而將原來的循環分給多個線程來做。(本程序是計算0~ArraySize-1的每個數中二進製包含1個數)。

  而對於一開始就打算用並行方法來實現的程序,用pthread應該是更方便和更清晰。

下麵是分別用pthread和openMP實現的worker_and_consumer:

pthread版:

代碼
複製代碼
 1 #include<unistd.h>
 2 #include<pthread.h>
 3 #include<stdio.h>
 4 #include<stdlib.h>
 5 
 6  #define SIZE 100
 7  #define THREAD_NUM_WORKER 15
 8 #define THREAD_NUM_CONSUMER 10
 9 #define SLEEP_WORKERS 2
10 #define SLEEP_CONSUMERS 1
11 
12 int warehouse[SIZE];
13 int at =-1;
14 int is_end =0;
15 pthread_mutex_t space = PTHREAD_MUTEX_INITIALIZER;
16 pthread_mutex_t end   = PTHREAD_MUTEX_INITIALIZER;
17 
18 void* consumer_func(void*);
19 void* worker_func(void*);
20 
21 int main()
22 {
23     pthread_t workers[THREAD_NUM_WORKER];
24     pthread_t consumers[THREAD_NUM_CONSUMER];
25     int i,j;
26     int n;
27     for(i=0;i<THREAD_NUM_WORKER;i++)
28         pthread_create(&workers[i],NULL,worker_func,NULL);
29     for(j=0;j<THREAD_NUM_CONSUMER;j++)
30         pthread_create(&consumers[j],NULL,consumer_func,NULL);
31     while(is_end==0)
32     {
33         scanf("%d",&n);
34         if(n==0)
35         {
36             pthread_mutex_lock(&end);
37             is_end=1;
38             pthread_mutex_unlock(&end);
39         }
40     }    
41     for(i=0;i<THREAD_NUM_WORKER;i++)
42             pthread_join(workers[i],NULL);
43     for(j=0;j<THREAD_NUM_CONSUMER;j++)
44             pthread_join(consumers[j],NULL);
45     return 0;
46 }
47 
48 void* worker_func(void* var)
49 {
50     while(1)
51     {
52         if(is_end)
53             break;
54         //保護at變量
55         pthread_mutex_lock(&space);
56         if(SIZE-at-1>0)
57         {
58             printf("Make %d by worker %lld ",warehouse[++at]=rand(),pthread_self());
59             printf("and at is %d\n",at);
60         }
61         pthread_mutex_unlock(&space);
62         sleep(SLEEP_WORKERS);
63     }
64     return NULL;
65 }
66 
67 
68 void* consumer_func(void* var)
69 {
70     while(1)
71     {
72         if(is_end)
73             break;
74         pthread_mutex_lock(&space);
75         if(at>=0)
76         {
77             printf("Got %d by consumer %lld\n",warehouse[at--],pthread_self());
78             printf("and at is %d\n",at);
79         }
80         pthread_mutex_unlock(&space);
81         sleep(SLEEP_CONSUMERS);
82     }
83     return NULL;
84 }
85 
86 
87 
88 
複製代碼

openMP版:

代碼
複製代碼
 1 #include<unistd.h>
 2 #include<stdio.h>
 3 #include<stdlib.h>
 4 #include<omp.h>
 5 
 6 
 7 #define SIZE 100
 8 #define THREAD_NUM_WORKER 15
 9 #define THREAD_NUM_CONSUMER 10
10 #define SLEEP_WORKERS 2
11 #define SLEEP_CONSUMERS 1
12 
13 int warehouse[SIZE];
14 int at =-1;
15 int is_end =0;
16 
17 void start_workers()
18 {
19     omp_set_num_threads(THREAD_NUM_WORKER);
20 #pragma omp parallel default(shared)
21     {
22         if(omp_get_thread_num()==0)
23             printf("worker num is %d\n",omp_get_num_threads());
24         while(1)
25         {
26                 if(is_end)
27                     break;
28 //保護at變量
29 #pragma omp critical(space)
30             {
31                 if(SIZE-at-1>0)
32                 {
33                     printf("Make %d by worker %d ",warehouse[++at]=rand(),omp_get_thread_num());
34                     printf("and at is %d\n",at);
35                 }
36             }
37             sleep(SLEEP_WORKERS);
38         }
39     }
40 }
41 
42 
43 void start_consumers(void)
44 {
45     omp_set_num_threads(THREAD_NUM_CONSUMER);
46 #pragma omp parallel default(shared)
47     {
48         if(omp_get_thread_num()==0)
49             printf("consumer num is %d\n",omp_get_num_threads());
50         while(1)
51         {
52             if(is_end)
53                 break;
54 #pragma omp critical(space)
55             {
56                 if(at>=0)
57                 {
58                     printf("Got %d by consumer %d\n",warehouse[at--],omp_get_thread_num());
59                     printf("and at is %d\n",at);
60                 }
61             }
62             sleep(SLEEP_CONSUMERS);
63         }
64     }
65 }
66 
67 int main()
68 {
69     omp_set_dynamic(0);
70     omp_set_nested(1);//這個不設置的話,就不能嵌套fork子線程咯
71     //先設置3個線程,每個線程完成一個section
72     omp_set_num_threads(3);
73 #pragma omp parallel sections
74     {
75 #pragma omp section
76         {
77             start_workers();
78         }
79 #pragma omp section
80         {
81             start_consumers();
82         }
83 #pragma omp section
84         {
85             int in;
86             scanf("%d",&in);
87             if(!in)
88             {
89 //保護is_end
90 #pragma omg critical(end)
91                 is_end =1;
92             }
93         }
94     }
95     return 0;
96 }
97 
98 
99 
複製代碼

  最後說一下,用openMP,編譯時要加上選項-fopenmp,編譯pthread時加上鏈接-lpthread。另外openMP有個缺點是若是代碼中編譯指令出錯時,找錯還是挺麻煩的,就像昨晚我把#pragma omp parallel寫成了#pragma omg parallel,結果編譯鏈接通過後卻始終隻有一個線程(主線程),找了好久...囧!

最後更新:2017-04-04 07:03:06

  上一篇:go 網絡瀏覽器統計分析
  下一篇:go Linux多線程編程小結