点击↑深色口袋物联,选择关注公众号,获取更多内容,不迷路
在glibc2.35中,pthread线程提供了5种同步机制,分别为条件变量、互斥锁、读写锁、自旋锁和屏障,本篇主要回顾屏障
在glibc2.35中,线程屏障的定义在sysdeps/nptl/bits/pthreadtypes.h中pthread_barrier_t
typedef union
{
char __size[__SIZEOF_PTHREAD_BARRIER_T];
long int __align;
} pthread_barrier_t;屏障的定义采用了和互斥锁、条件变量一样的 “不透明类型” 设计模式。屏障的逻辑非常复杂,它内部需要记录“需要等待的总线程数”、“当前已到达的线程数”、“当前的阶段(第几轮同步)”以及“用于挂起线程的队列”。glibc 开发者不希望用户直接操作这些字段,所以隐藏在 char 数组后面
屏障的核心作用:实现多线程的「同步会合」,保证一组线程在某一阶段完成后,再统一进入下一阶段
pthread_barrier_wait(),线程进入阻塞状态;简单说:屏障解决了「多线程阶段式协作」的问题,比如「所有线程完成数据加载后,再统一进行计算」「所有线程完成初始化后,再统一开始任务」,是比条件变量更简洁的「多线程会合」方案。
在多线程阶段式任务中,传统同步方式(互斥锁 + 条件变量)存在明显缺陷:
count;pthread_barrier_wait(),内核原子性将「已到达数」+1;PTHREAD_BARRIER_SERIAL_THREAD(可作为「主线程」执行汇总逻辑),其余线程返回 0。屏障的核心价值是「多线程会合」,所有场景都围绕「阶段式任务」展开,覆盖 Linux 多线程开发的高频协作场景,在匹配场景下,是代码最简洁、效率最高的同步方案,按使用频率排序如下:
sleep同步,屏障能精准控制线程启动时间,测试结果更准确。PTHREAD_PROCESS_SHARED属性,实现跨进程同步。屏障的接口全部在 <pthread.h> 头文件中,编译时必须加 -lpthread 链接线程库,所有接口返回值:成功返回 0,失败返回错误码(非 errno,需手动处理)。
其中参数3, count,核心参数,表示需要等待的目标线程数(必须 > 0,否则返回 EINVAL);count一旦设置,默认无法修改(部分系统支持属性修改)。
int pthread_barrier_init(pthread_barrier_t *restrict barrier,
const pthread_barrierattr_t *restrict attr,
unsigned int count);释放屏障占用的内核资源(等待队列、计数器等);
其中:① 必须与pthread_barrier_init 成对调用; ② 屏障有线程等待时不能销毁(返回 EBUSY 错误);③ 销毁后的屏障若需复用,需重新初始化。
int pthread_barrier_destroy(pthread_barrier_t *barrier);调用线程进入屏障等待,直到所有count个线程都调用该函数;所有线程到达后,所有线程同时被唤醒;
int pthread_barrier_wait(pthread_barrier_t *barrier);返回值有3种情况:
PTHREAD_BARRIER_SERIAL_THREAD「串行线程」(仅一个线程返回此值),可用于执行汇总、清理等独有的逻辑二者都是「同步机制」,核心差异在「多线程会合」的实现复杂度:
✅ 核心结论:多线程会合用屏障,两个线程 / 条件触发用条件变量。
二者是「同步 vs 互斥」的本质区别:
✅ 核心结论:同步会合用屏障,资源保护用互斥锁。
✅ 选型原则:阶段式同步用屏障,多读少写用读写锁。
✅ 核心结论:优先使用单次屏障(兼容性),高频复用可尝试循环屏障。
代码才是硬道理,下面举3个例子
实现最经典的「多线程初始化会合」场景:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#define THREAD_NUM 5 // 参与屏障的线程数
// 屏障(全局变量,方便所有线程访问)
pthread_barrier_t barrier;
// 线程函数:模拟数据加载+屏障会合
void* load_data(void* arg)
{
int tid = *(int*)arg;
printf("线程%d:开始加载数据源%d...\n", tid, tid);
// 模拟数据加载耗时(1-3秒随机)
sleep(1 + rand() % 3);
printf("线程%d:数据源%d加载完成,到达屏障等待...\n", tid, tid);
// 等待屏障:所有线程到达后继续
int ret = pthread_barrier_wait(&barrier);
if (ret == PTHREAD_BARRIER_SERIAL_THREAD) {
// 仅一个线程执行汇总逻辑
printf("=====================================\n");
printf("串行线程(线程%d):所有数据源加载完成,执行初始化汇总!\n", tid);
printf("=====================================\n");
} else if (ret != 0) {
fprintf(stderr, "线程%d:屏障等待失败,错误码=%d\n", tid, ret);
pthread_exit(NULL);
}
// 所有线程统一进入业务处理阶段
printf("线程%d:进入业务处理阶段...\n", tid);
sleep(1); // 模拟业务处理
printf("线程%d:业务处理完成\n", tid);
return NULL;
}
int main()
{
pthread_t tid[THREAD_NUM];
int tid_arr[THREAD_NUM] = {1,2,3,4,5};
// 初始化随机数种子
srand((unsigned int)time(NULL));
// 初始化屏障:目标线程数=THREAD_NUM
int ret = pthread_barrier_init(&barrier, NULL, THREAD_NUM);
if (ret != 0) {
fprintf(stderr, "屏障初始化失败,错误码=%d\n", ret);
return -1;
}
// 创建5个线程
for (int i=0; i<THREAD_NUM; i++) {
ret = pthread_create(&tid[i], NULL, load_data, &tid_arr[i]);
if (ret != 0) {
fprintf(stderr, "创建线程%d失败,错误码=%d\n", i+1, ret);
return -1;
}
}
// 等待所有线程完成
for (int i=0; i<THREAD_NUM; i++) {
pthread_join(tid[i], NULL);
}
// 销毁屏障(确保所有线程已离开屏障)
ret = pthread_barrier_destroy(&barrier);
if (ret != 0) {
fprintf(stderr, "屏障销毁失败,错误码=%d\n", ret);
return -1;
}
printf("主线程:所有任务完成,程序正常退出\n");
return 0;
}运行结果
count严格等于线程数,无永久阻塞风险。线程1:开始加载数据源1...
线程2:开始加载数据源2...
线程3:开始加载数据源3...
线程4:开始加载数据源4...
线程5:开始加载数据源5...
线程1:数据源1加载完成,到达屏障等待...
线程3:数据源3加载完成,到达屏障等待...
线程2:数据源2加载完成,到达屏障等待...
线程5:数据源5加载完成,到达屏障等待...
线程4:数据源4加载完成,到达屏障等待...
=====================================
串行线程(线程4):所有数据源加载完成,执行初始化汇总!
=====================================
线程4:进入业务处理阶段...
线程1:进入业务处理阶段...
线程3:进入业务处理阶段...
线程2:进入业务处理阶段...
线程5:进入业务处理阶段...
线程4:业务处理完成
线程1:业务处理完成
线程3:业务处理完成
线程2:业务处理完成
线程5:业务处理完成
主线程:所有任务完成,程序正常退出实现多阶段任务的屏障复用,覆盖「销毁→重新初始化」的核心规则:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#define THREAD_NUM 4 // 线程数
#define PHASE_NUM 3 // 任务阶段数
// 屏障(每个阶段复用)
pthread_barrier_t barrier;
// 阶段名称
const char* phase_names[] = {"数据采集", "数据清洗", "结果输出"};
// 线程函数:多阶段任务+屏障会合
void* process_task(void* arg)
{
int tid = *(int*)arg;
for (int phase=0; phase<PHASE_NUM; phase++) {
// 执行当前阶段任务
printf("线程%d:开始%s阶段...\n", tid, phase_names[phase]);
usleep(500000 + rand() % 500000); // 模拟任务耗时(0.5-1秒)
printf("线程%d:%s阶段完成,到达屏障等待...\n", tid, phase_names[phase]);
// 等待屏障:所有线程完成当前阶段
int ret = pthread_barrier_wait(&barrier);
if (ret == PTHREAD_BARRIER_SERIAL_THREAD) {
printf("=====================================\n");
printf("串行线程(线程%d):所有线程完成【%s】阶段!\n", tid, phase_names[phase]);
printf("=====================================\n");
} else if (ret != 0) {
fprintf(stderr, "线程%d:第%d阶段屏障等待失败,错误码=%d\n", tid, phase+1, ret);
pthread_exit(NULL);
}
// 阶段切换:销毁当前屏障,为下一阶段初始化(最后一阶段无需)
if (phase < PHASE_NUM - 1) {
pthread_barrier_destroy(&barrier);
pthread_barrier_init(&barrier, NULL, THREAD_NUM);
}
}
printf("线程%d:所有阶段完成,退出\n", tid);
return NULL;
}
int main()
{
pthread_t tid[THREAD_NUM];
int tid_arr[THREAD_NUM] = {1,2,3,4};
// 初始化随机数种子
srand((unsigned int)time(NULL));
// 初始化第一阶段的屏障
int ret = pthread_barrier_init(&barrier, NULL, THREAD_NUM);
if (ret != 0) {
fprintf(stderr, "屏障初始化失败,错误码=%d\n", ret);
return -1;
}
// 创建线程
for (int i=0; i<THREAD_NUM; i++) {
pthread_create(&tid[i], NULL, process_task, &tid_arr[i]);
}
// 等待所有线程完成
for (int i=0; i<THREAD_NUM; i++) {
pthread_join(tid[i], NULL);
}
// 销毁最后一阶段的屏障
pthread_barrier_destroy(&barrier);
printf("主线程:所有阶段任务完成,程序正常退出\n");
return 0;
}运行结果
线程1:开始数据采集阶段...
线程2:开始数据采集阶段...
线程3:开始数据采集阶段...
线程4:开始数据采集阶段...
线程1:数据采集阶段完成,到达屏障等待...
线程2:数据采集阶段完成,到达屏障等待...
线程3:数据采集阶段完成,到达屏障等待...
线程4:数据采集阶段完成,到达屏障等待...
=====================================
串行线程(线程4):所有线程完成【数据采集】阶段!
=====================================
线程1:开始数据清洗阶段...
线程2:开始数据清洗阶段...
线程3:开始数据清洗阶段...
线程4:开始数据清洗阶段...
...(中间阶段略)
=====================================
串行线程(线程2):所有线程完成【结果输出】阶段!
=====================================
线程1:所有阶段完成,退出
线程2:所有阶段完成,退出
线程3:所有阶段完成,退出
线程4:所有阶段完成,退出
主线程:所有阶段任务完成,程序正常退出实现跨进程的屏障同步,覆盖「共享属性设置」「共享内存」核心知识点:
PTHREAD_PROCESS_SHARED);#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#define THREAD_NUM_PER_PROC 2 // 每个进程的线程数
#define PROC_NUM 2 // 进程数
#define TOTAL_THREAD_NUM (THREAD_NUM_PER_PROC * PROC_NUM) // 总线程数
// 共享内存中的数据结构
typedef struct {
pthread_barrier_t barrier; // 跨进程屏障
int flag; // 同步标志
} SharedData;
// 共享内存指针
SharedData* shared_data;
// 线程函数:跨进程屏障等待
void* thread_func(void* arg)
{
int tid = *(int*)arg;
pid_t pid = getpid();
printf("进程%d线程%d:开始执行任务...\n", pid, tid);
sleep(1); // 模拟任务耗时
printf("进程%d线程%d:任务完成,到达跨进程屏障等待...\n", pid, tid);
// 等待跨进程屏障
int ret = pthread_barrier_wait(&shared_data->barrier);
if (ret == PTHREAD_BARRIER_SERIAL_THREAD) {
printf("=====================================\n");
printf("进程%d线程%d(串行线程):所有进程的线程都已到达屏障!\n", pid, tid);
printf("=====================================\n");
shared_data->flag = 1; // 设置同步标志
} else if (ret != 0) {
fprintf(stderr, "进程%d线程%d:屏障等待失败,错误码=%d\n", pid, tid, ret);
pthread_exit(NULL);
}
// 验证同步标志
printf("进程%d线程%d:同步标志=%d,继续执行...\n", pid, tid, shared_data->flag);
return NULL;
}
// 进程函数:创建线程并执行
void proc_func()
{
pthread_t tid[THREAD_NUM_PER_PROC];
int tid_arr[THREAD_NUM_PER_PROC] = {1,2};
// 创建线程
for (int i=0; i<THREAD_NUM_PER_PROC; i++) {
pthread_create(&tid[i], NULL, thread_func, &tid_arr[i]);
}
// 等待线程完成
for (int i=0; i<THREAD_NUM_PER_PROC; i++) {
pthread_join(tid[i], NULL);
}
}
int main()
{
int fd;
// 创建共享内存(大小为SharedData)
fd = shm_open("/my_shared_barrier", O_CREAT | O_RDWR, 0666);
if (fd == -1) {
perror("shm_open failed");
return -1;
}
ftruncate(fd, sizeof(SharedData));
// 映射共享内存到进程地址空间
shared_data = (SharedData*)mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shared_data == MAP_FAILED) {
perror("mmap failed");
return -1;
}
// 初始化跨进程屏障属性(共享属性)
pthread_barrierattr_t attr;
pthread_barrierattr_init(&attr);
pthread_barrierattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
// 初始化屏障:总线程数=TOTAL_THREAD_NUM
pthread_barrier_init(&shared_data->barrier, &attr, TOTAL_THREAD_NUM);
pthread_barrierattr_destroy(&attr);
// 初始化同步标志
shared_data->flag = 0;
// 创建子进程
pid_t pid = fork();
if (pid == -1) {
perror("fork failed");
return -1;
} else if (pid == 0) {
// 子进程:执行线程任务
proc_func();
exit(0);
}
// 父进程:执行线程任务
proc_func();
// 等待子进程退出
waitpid(pid, NULL, 0);
// 清理资源
pthread_barrier_destroy(&shared_data->barrier);
munmap(shared_data, sizeof(SharedData));
shm_unlink("/my_shared_barrier");
printf("主线程:跨进程屏障同步完成,程序正常退出\n");
return 0;
}运行结果
PTHREAD_PROCESS_SHARED属性,实现跨进程同步;进程12345线程1:开始执行任务...
进程12345线程2:开始执行任务...
进程12346线程1:开始执行任务...
进程12346线程2:开始执行任务...
进程12345线程1:任务完成,到达跨进程屏障等待...
进程12345线程2:任务完成,到达跨进程屏障等待...
进程12346线程1:任务完成,到达跨进程屏障等待...
进程12346线程2:任务完成,到达跨进程屏障等待...
=====================================
进程12346线程2(串行线程):所有进程的线程都已到达屏障!
=====================================
进程12346线程2:同步标志=1,继续执行...
进程12345线程1:同步标志=1,继续执行...
进程12345线程2:同步标志=1,继续执行...
进程12346线程1:同步标志=1,继续执行...
主线程:跨进程屏障同步完成,程序正常退出timedwait接口,无法设置等待超时,易导致永久阻塞;✔️ 屏障的优点是压倒性的:在多线程阶段式会合场景下,是代码最简洁、效率最高的同步方案,没有任何技术能替代;
✔️ 屏障的缺点都是可规避的:通过提前确定线程数、手动重置复用、禁用线程取消,能轻松解决所有局限性;
✔️ 结论:屏障是 Linux 多线程阶段式协作「必学、必会」的同步技术,核心是「场景匹配 + 规则遵守」。