pthread在程序启动时创建一束线程,将工作分配到线程上。然而,这种方法需要相当多的线程指定代码,而且不能保证能够随着可用处理器的数量而合理地进行扩充。
OpenMP,不需要指定数量,在有循环的地方加上代码,修改设置文件极客。OpenMP 非常方便,因为它不会将软件锁定在事先设定的线程数量中,但是相对的查错更难也更麻烦。
===================================================================================
首先mpi是基于分布式内存系统,而openmp和pthread基于共享内存系统;
也就是说mpi之间的数据共享需要通过消息传递,因为mpi同步的程序属于不同的进程,甚至不同的主机上的不同进程。 相反由于openmp和pthread共享内存,不同线程之间的数据就无须传递,直接传送指针就行。
同时mpi不同主机之间的进程协调工作需要安装mpi软件(例如mpich)来完成。
在openmp和pthread之间的区别主要在编译的方式上,openmp的编译需要添加编译器预处理指令#pragma,创建线程等后续工作要编译器来完成。
而pthread就是一个库,所有的并行线程创建都需要我们自己完成,较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版:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
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版:
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
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,结果编译链接通过后却始终只有一个线程(主线程),找了好久...囧!