首页 > C/C++ > 线程 > Linux多线程编程(九)屏障
2017
08-21

Linux多线程编程(九)屏障

九、 屏障

  1、 基本概念

  2、 API

    1)、 创建屏障

    2)、 等待

    3)、 销毁屏障

 

 

九、屏障

1、基本概念

barrier(屏障)与互斥量,读写锁,自旋锁不同,它不是用来保护临界区的。相反,它跟条件变量一样,是用户协调多个线程并行工作的同步机制。屏障允许每个线程等待,直到所有的合作线程都达到某一点,然后从该点继续执行。

条件变量是多线程间传递状态的改变来达到协同工作的效果。屏障是多线程各自做自己的工作,如果某一线程完成了工作,就等待在屏障那里,直到其他线程的工作都完成了,再一起做别的事。举个通俗的例子:

1.对于条件变量。在接力赛跑里,1号队员开始跑的时候,2,3,4号队员都站着不动,直到1号队员跑完一圈,把接力棒给2号队员,2号队员收到接力棒后就可以跑了,跑完再给3号队员。这里这个接力棒就相当于条件变量,条件满足后就可以由下一个队员(线程)跑。

2.对于屏障。在百米赛跑里,比赛没开始之前,每个运动员都在赛场上自由活动,有的热身,有的喝水,有的跟教练谈论。比赛快开始时,准备完毕的运动员就预备在起跑线上,如果有个运动员还没准备完(除去特殊情况),他们就一直等,直到运动员都在起跑线上,裁判喊口号后再开始跑。这里的起跑线就是屏障,做完准备工作的运动员都等在起跑线,直到其他运动员也把准备工作做完!

2、API

POSIX定义的自旋锁的数据类型是: pthread_spinlock_t
相关API

  1. #include <pthread.h>  
  1. int  pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);成功返回0,其它返回值表示出错  
  2. int pthread_barrier_wait(pthread_barrier_t *barrier); 
  3. int pthread_barrier_destroy(pthread_barrier_t *barrier);成功返回0,其它返回值表示出错  

1)、创建屏障

  1. #include <pthread.h>
  2. int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count);成功返回0,其它返回值表示出错

 

函数的返回值:若成功,返回0;否则,返回错误编号。。

barrier:pthread_barrier_t结构体指针

attr:屏障属性结构体指针

count:屏障等待的线程数目,即要count个线程都到达屏障时,屏障才解除,线程就可以继续执行

2)、等待

  1. #include <pthread.h>
  2. int pthread_barrier_wait(pthread_barrier_t *barrier);

 

返回值:若成功,返回0或者PTHREAD_BARRIER_SERIAL_THREAD;否则,返回错误编号

 

调用pthread_barrier_wait的线程在屏障技术count未满足条件时,会进入休眠状态。如果该线程是最后一个调用pthread_barrier_wait的线程,就满足了屏障计数,所有的线程都被唤醒。

对于一个任意线程,pthread_barrier_wait函数返回PTHREAD_BARRIER_SERIAL_THREAD。剩下的线程看到的返回值是0。这使得一个线程可以作为主线程,它可以工作在其他所有线程已完成的工作结果上。

3)、销毁屏障

  1. #include <pthread.h>
  2. int pthread_barrier_destroy(pthread_barrier_t *barrier);

 

函数的返回值:若成功,返回0;否则,返回错误编号。

当一个屏障被使用完(即屏障已经等待了足够数目的线程后)就应该被销毁,故一个屏障我们不能复用,除非我们将它销毁并重新初始化。

 

在这里我们需要注意的是使用barrier这个屏障我们无法获取线程的结束状态,若想要获取相关线程结束状态我们仍然需要调用pthread_join函数。当然我们一般也不会把pthread_barrier_wait 放在某个线程结束时候,这显然是很无聊的,这个函数调用往往出现在线程之间的某个位置,接下来线程等待其它协同线程到达屏障后再处理一些其它事务。

 

实例:

/**/

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <unistd.h>
  5. #include <pthread.h>
  6.  
  7. /* 屏障总数 */
  8. #define PTHREAD_BARRIER_SIZE 4
  9.  
  10. /* 定义屏障 */
  11. pthread_barrier_t barrier;
  12.  
  13. void err_exit(const char *err_msg)
  14. {
  15.     printf("error:%s\n", err_msg);
  16.     exit(1);
  17. }
  18.  
  19. void *thread_fun(void *arg)
  20. {
  21.     int result;
  22.     char *thr_name = (char *)arg;
  23.  
  24.     /* something work */
  25.  
  26.     printf("线程%s工作完成…\n", thr_name);
  27.  
  28.     /* 等待屏障 */
  29.     result = pthread_barrier_wait(&barrier);
  30.     if (result == PTHREAD_BARRIER_SERIAL_THREAD)
  31.         printf("线程%s,wait后第一个返回\n", thr_name);
  32.     else if (result == 0)
  33.         printf("线程%s,wait后返回为0\n", thr_name);
  34.  
  35.     return NULL;
  36. }
  37.  
  38. int main(void)
  39. {
  40.     pthread_t tid_1, tid_2, tid_3;
  41.  
  42.     /* 初始化屏障 */
  43.     pthread_barrier_init(&barrier, NULL, PTHREAD_BARRIER_SIZE);
  44.  
  45.     if (pthread_create(&tid_1, NULL, thread_fun, "1") != 0)
  46.         err_exit("create thread 1");
  47.  
  48.     if (pthread_create(&tid_2, NULL, thread_fun, "2") != 0)
  49.         err_exit("create thread 2");
  50.  
  51.     if (pthread_create(&tid_3, NULL, thread_fun, "3") != 0)
  52.         err_exit("create thread 3");
  53.  
  54.     /* 主线程等待工作完成 */
  55.     pthread_barrier_wait(&barrier);
  56.     printf("所有线程工作已完成…\n");
  57.  
  58.     sleep(1);
  59.     return 0;
  60. }
  61.  

 

运行结果为:

  1. vteyga@root:/root> ./pthread_barrier
  2. 线程2工作完成…
  3. 线程3工作完成…
  4. 线程1工作完成…
  5. 线程1,wait后第一个返回
  6. 所有线程工作已完成…
  7. 线程2,wait后返回为0
  8. 线程3,wait后返回为0
  9.  
最后编辑:
作者:liujg
真实-不弄虚,不做假,做自己,不违心; 踏实-不浮躁,不盲从,不急功,不近利; 实学-不投机,不取巧,勤于学,精于业。