一.概述 barrier(屏障)與互斥量,讀寫鎖,自旋鎖不同,它不是用來保護臨界區的。相反,它跟條件變數一樣,是用來協同多線程一起工作!!!條件變數是多線程間傳遞狀態的改變來達到協同工作的效果。屏障是多線程各自做自己的工作,如果某一線程完成了工作,就等...
一.概述
barrier(屏障)與互斥量,讀寫鎖,自旋鎖不同,它不是用來保護臨界區的。相反,它跟條件變數一樣,是用來協同多線程一起工作!!!
條件變數是多線程間傳遞狀態的改變來達到協同工作的效果。屏障是多線程各自做自己的工作,如果某一線程完成了工作,就等待在屏障那裡,直到其他線程的工作都完成了,再一起做別的事。舉個通俗的例子:
1.對於條件變數。在接力賽跑里,1號隊員開始跑的時候,2,3,4號隊員都站著不動,直到1號隊員跑完一圈,把接力棒給2號隊員,2號隊員收到接力棒後就可以跑了,跑完再給3號隊員。這裡這個接力棒就相當於條件變數,條件滿足後就可以由下一個隊員(線程)跑。
2.對於屏障。在百米賽跑里,比賽沒開始之前,每個運動員都在賽場上自由活動,有的熱身,有的喝水,有的跟教練談論。比賽快開始時,準備完畢的運動員就預備在起跑線上,如果有個運動員還沒準備完(除去特殊情況),他們就一直等,直到運動員都在起跑線上,裁判喊口號後再開始跑。這裡的起跑線就是屏障,做完準備工作的運動員都等在起跑線,直到其他運動員也把準備工作做完!
二.函數介面
1.創建屏障
1 #include <pthread.h> 2 3 int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);
barrier:pthread_barrier_t結構體指針
attr:屏障屬性結構體指針
count:屏障等待的線程數目,即要count個線程都到達屏障時,屏障才解除,線程就可以繼續執行
2.等待
1 #include <pthread.h> 2 3 int pthread_barrier_wait(pthread_barrier_t *barrier);
函數的成功返回值有2個,第一個成功返回的線程會返回PTHREAD_BARRIER_SERIAL_THREAD,其他線程都返回0。可以用第一個成功返回的線程來做一些善後處理工作。
3.銷毀屏障
1 #include <pthread.h> 2 3 int pthread_barrier_destroy(pthread_barrier_t *barrier);
三.簡單例子
寫個簡單的例子,主線程等待其他線程都完成工作後自己再向下執行,類似pthread_join()函數!
1 /** 2 * @file pthread_barrier.c 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <pthread.h> 10 11 /* 屏障總數 */ 12 #define PTHREAD_BARRIER_SIZE 4 13 14 /* 定義屏障 */ 15 pthread_barrier_t barrier; 16 17 void err_exit(const char *err_msg) 18 { 19 printf("error:%s\n", err_msg); 20 exit(1); 21 } 22 23 void *thread_fun(void *arg) 24 { 25 int result; 26 char *thr_name = (char *)arg; 27 28 /* something work */ 29 30 printf("線程%s工作完成...\n", thr_name); 31 32 /* 等待屏障 */ 33 result = pthread_barrier_wait(&barrier); 34 if (result == PTHREAD_BARRIER_SERIAL_THREAD) 35 printf("線程%s,wait後第一個返回\n", thr_name); 36 else if (result == 0) 37 printf("線程%s,wait後返回為0\n", thr_name); 38 39 return NULL; 40 } 41 42 int main(void) 43 { 44 pthread_t tid_1, tid_2, tid_3; 45 46 /* 初始化屏障 */ 47 pthread_barrier_init(&barrier, NULL, PTHREAD_BARRIER_SIZE); 48 49 if (pthread_create(&tid_1, NULL, thread_fun, "1") != 0) 50 err_exit("create thread 1"); 51 52 if (pthread_create(&tid_2, NULL, thread_fun, "2") != 0) 53 err_exit("create thread 2"); 54 55 if (pthread_create(&tid_3, NULL, thread_fun, "3") != 0) 56 err_exit("create thread 3"); 57 58 /* 主線程等待工作完成 */ 59 pthread_barrier_wait(&barrier); 60 printf("所有線程工作已完成...\n"); 61 62 sleep(1); 63 return 0; 64 }
28行是線程自己要做的工作,62行的sleep(1)讓所有線程有足夠的時間把自己的返回值列印出來。編譯運行:
可以看到,3個線程工作完成後才可以越過屏障列印返回值,第一個返回的是PTHREAD_BARRIER_SERIAL_THREAD,其他都是0。
這裡有一點要註意:我們從運行結果看出,主線程列印"所有線程工作已完成"之後,線程1,線程2還在運行列印返回值。這個結果難免會誤解"主線程等待所有線程完成工作之後再向下執行"。區分一點即可:等待只針對屏障之前的動作,越過屏障後,無論是主線程,還是子線程都會併發執行,如果非要讓子線程完完全全執行完,可以再加個屏障到線程函數末尾,相應主線程也要加!