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,结果编译链接通过后却始终只有一个线程(主线程),找了好久...囧!

你可能感兴趣的:(pthread)