linux barrier 栅栏屏障,让多任务在栅栏处集合,全部到齐后同时出发

专栏内容
postgresql内核源码分析
手写数据库toadb
并发编程
个人主页:我的主页
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

==================================

概述

pthread_barrier_t 这是posix定义线程同步方法,不一定所有linux 版本中都实现了它。

barrier 是一种非常有效的线程同步方法,当我们需要几个线程一起开始时,或者在某个条件下需要一起等待时,
就需要有个类似栅栏一样的东西,条件成立时,就会被拦住。

当然这个功能,也可能通过管道,信号量,eventfd等方法实现,但是barrier非常简单高效。

接口

/* 头文件 */
#include 

/* 初始化接口 */ 
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); 

接口说明

  1. pthread_barrier_init接口会初始化所需资源;

如果attr传入为NULL时,采用默认值进行初始化;

count ,是指调用 pthread_barrier_wait多少次,才能成功返回;其值必须大于0 ;

只有返回成功,才是初始化完成;

  • 返回码
  • EAGAIN 系统资源不足

  • EINVAL 非法入参,count必须大于0

  • ENOMEM 内存不足
  1. pthread_barrier_destroy 接口销毁所有barrier上分配的资源
  1. 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,那么所有阻塞线程,在信号处理完成后,才会一起放开执行;

举例

代码

#include 
#include 
#include 

#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个人,那么凑够三个人的组就可以开始比赛了,还没凑够的就不能比赛;

编译

[senllang@hatch barrier]$ gcc ex01_barrier.c -lpthread -o barrier

多线程,在编译时,需要加线程库 -lpthread

运行结果

[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强制停止了

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

你可能感兴趣的:(并发编程,linux,数据库,服务器,c语言,并发编程,分布式,后端)