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

专栏内容
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); 

接口说明

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

如果attr传入为NULL时,采用默认值进行初始化;
count ,是指调用 pthread_barrier_wait多少次,才能成功返回;其值必须大于0 ;
只有返回成功,才是初始化完成;

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

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

  • ENOMEM 内存不足
  1. pthread_barrier_destroy 接口销毁所有barrier上分配的资源

  2. 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

如有错误或者疏漏欢迎指出,互相学习。

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

相关推荐
Peace8 分钟前
【Nginx】
linux·运维·nginx
Java爱好狂.19 分钟前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
网络与设备以及操作系统学习使用者38 分钟前
Linux与Windows核心差异深度解析
linux·运维·网络·windows·学习
陈随易38 分钟前
Redis 8.8发布,一定要更新
前端·后端·程序员
basketball61643 分钟前
SQL 常用数据格式化操作方法总结
数据库·sql
装不满的克莱因瓶1 小时前
SpringBoot 如何将 lib 目录中jar包打包进最终的jar包里面
spring boot·后端·maven·jar·mvn
TE-茶叶蛋1 小时前
数据库-引用完整性(referential integrity)
数据库
筠筠喵呜喵1 小时前
Linux CPU性能优化:D状态和Z状态排查与处理
linux·服务器·性能优化
IronMurphy1 小时前
MySQL拷打第二讲
数据库·mysql
ltl2 小时前
Transformer 原论文实验结果:为什么 28.4 BLEU 足以改写路线图
后端