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

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

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

相关推荐
陈大爷(有低保)4 分钟前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、4 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头5 分钟前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_8574396917 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66618 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
粤海科技君24 分钟前
如何使用腾讯云GPU云服务器自建一个简单的类似ChatGPT、Kimi的会话机器人
服务器·chatgpt·机器人·腾讯云
weixin_4493108425 分钟前
高效集成:聚水潭采购数据同步到MySQL
android·数据库·mysql
傲骄鹿先生34 分钟前
阿里云centos7.9服务器磁盘挂载,切换服务路径
服务器·阿里云·磁盘
阿华的代码王国38 分钟前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
有谁看见我的剑了?1 小时前
Ubuntu 22.04.5 配置vlan子接口和网桥
服务器·网络·ubuntu