专栏内容 :
postgresql内核源码分析
手写数据库toadb
并发编程
个人主页 :我的主页
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.
==================================
概述
pthread_barrier_t 这是posix定义线程同步方法,不一定所有linux 版本中都实现了它。
barrier 是一种非常有效的线程同步方法,当我们需要几个线程一起开始时,或者在某个条件下需要一起等待时,
就需要有个类似栅栏一样的东西,条件成立时,就会被拦住。
当然这个功能,也可能通过管道,信号量,eventfd等方法实现,但是barrier非常简单高效。
接口
c
/* 头文件 */
#include <pthread.h>
/* 初始化接口 */
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr, unsigned count);
/* 销毁接口,资源回收 */
int pthread_barrier_destroy(pthread_barrier_t *barrier);
/* 栅栏接口,调用者会阻塞,直到调用次数达到 count值后,所有阻塞都会放开 */
int pthread_barrier_wait(pthread_barrier_t *barrier);
接口说明
- pthread_barrier_init接口会初始化所需资源;
如果attr传入为NULL时,采用默认值进行初始化;
count ,是指调用 pthread_barrier_wait多少次,才能成功返回;其值必须大于0 ;
只有返回成功,才是初始化完成;
- 返回码
EAGAIN 系统资源不足
EINVAL 非法入参,count必须大于0
- ENOMEM 内存不足
-
pthread_barrier_destroy 接口销毁所有barrier上分配的资源
-
pthread_barrier_wait 会设置屏障栅栏,所有调用处都会被阻塞住,直到调用次数到达count值时,才会继续执行;
对于超过count的调用,不会被阻栏,那么它们有可能会先于栅栏阻塞的线程执行;这是由系统调度来决定的;
当调用达到count次数后,又会被重置;
注意事项
pthread_barrier_t使用前必须初始化,否则行为是未定义的;
当正在使用的pthread_barrier_t 调用了 pthread_barrier_destroy,后面的行为也是未定义的;
尤其是,当有线程阻塞在 barrier上时;
- 当正在使用的pthread_barrier_t调用了pthread_barrier_init,那就会被重新初始化,后面的行为也是未定义的;
正此种情况下,对于 pthread_barrier_wait 调用不会返回;
这些接口不会返回 EINTR 错误码;
pthread_barrier_wait 如果遇到信号中断处理,对于已经被阻塞的线程中有正在处理信号,
如果还没有到达count,信号处理返回后会继续等待;
如果信号处理过程中,已经到达count,那么所有阻塞线程,在信号处理完成后,才会一起放开执行;
举例
代码
c
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#define THEAD_COUNT 10
#define THRED_WAIT_COUNT 3
pthread_barrier_t start_barrier;
int index[THEAD_COUNT];
void* func(void *data)
{
int i = *(int *)data;
int wait = i/THRED_WAIT_COUNT * 1000;
printf("threads %p starting %d \n", pthread_self(), i);
/* thread-0 will wait longer time than the other threads. */
usleep(wait);
pthread_barrier_wait(&start_barrier);
printf("threads %p running %d \n", pthread_self(), i);
}
int main(int argc, char *argv[])
{
int ret;
int count = THRED_WAIT_COUNT;
int i = 0;
pthread_t threads[THEAD_COUNT];
pthread_barrier_init(&start_barrier, NULL, count);
for (i = 0; i < THEAD_COUNT; i++)
{
index[i] = i;
ret = pthread_create(&threads[i], NULL, func, (void*)&index[i]);
if (ret != 0)
{
printf("failed to create thread: %d\n", i);
}
}
for (i = 0; i < THEAD_COUNT; i++)
{
ret = pthread_join(threads[i], NULL);
if (ret != 0)
{
printf("failed to join thread: %d\n", i);
}
}
printf("all threads exited \n");
pthread_barrier_destroy(&start_barrier);
return 0;
}
- 代码说明
在代码中启动了10个线程,但是设置barrier的count只有3个
也就是说只需要凑够3个线程,就可以开启了;
这就像我们报名参赛,每组需要3个人,那么凑够三个人的组就可以开始比赛了,还没凑够的就不能比赛;
编译
shell
[senllang@hatch barrier]$ gcc ex01_barrier.c -lpthread -o barrier
多线程,在编译时,需要加线程库 -lpthread
运行结果
shell
[senllang@hatch barrier]$ ./barrier
threads 0x7fc85a523700 starting 0
threads 0x7fc859d22700 starting 1
threads 0x7fc859521700 starting 2
threads 0x7fc850d20700 starting 3
threads 0x7fc858d20700 starting 4
threads 0x7fc859521700 running 2
threads 0x7fc85a523700 running 0
threads 0x7fc859d22700 running 1
threads 0x7fc853fff700 starting 5
threads 0x7fc8537fe700 starting 6
threads 0x7fc852ffd700 starting 7
threads 0x7fc8527fc700 starting 8
threads 0x7fc851ffb700 starting 9
threads 0x7fc853fff700 running 5
threads 0x7fc850d20700 running 3
threads 0x7fc858d20700 running 4
threads 0x7fc8527fc700 running 8
threads 0x7fc8537fe700 running 6
threads 0x7fc852ffd700 running 7
^C
[senllang@hatch barrier]$
- 运行结果说明
在代码中,为了分组明显,传入了每个线程的序号,根据序号每组等待的时间不同;
可以看到每三个是一组,到达三的倍数时,就开始有starting的线程
但是最后有两个线程,凑不够三个,所以一直没有开始运行
只能用ctrl+c强制停止了
结尾
非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!
作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。
注:未经同意,不得转载!