1.介绍
在并发编程中,屏障(Barrier)是一种同步原语,它用于在并发执行的线程之间创建一个同步点。线程到达屏障后会停止执行,直到所有的线程都到达这个屏障。一旦所有线程都到达屏障,它们就可以继续并发执行。屏障通常用于确保在并发计算中的某个阶段所有的线程都已经完成工作,然后再开始下一个阶段。
例如,假设有一个并行算法,它的执行可以分为两个阶段:阶段A和阶段B。阶段B的计算依赖于阶段A的结果。在这种情况下,我们就可以在阶段A和阶段B之间设置一个屏障,以确保所有线程都完成了阶段A的计算,然后再开始阶段B的计算。
在 POSIX 线程库中,屏障由 pthread_barrier_t
类型表示,可以通过 pthread_barrier_init
函数创建,并通过 pthread_barrier_destroy
函数销毁。线程可以通过调用 pthread_barrier_wait
函数来到达屏障,这个函数会阻塞线程,直到所有的线程都到达屏障。一旦所有线程都到达屏障,pthread_barrier_wait
函数返回,所有的线程都可以继续执行。
总的来说,屏障是一种重要的同步原语,它在处理并行计算问题,特别是那些需要分阶段进行的计算问题时非常有用。
2.C库接口
-
初始化:
pthread_barrier_init
函数用于初始化一个屏障:
int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count);
这个函数接受一个指向 pthread_barrier_t
类型的指针 barrier
,一个指向 pthread_barrierattr_t
类型的指针 attr
,以及一个无符号整数 count
。attr
参数可以设置屏障的属性,或者设置为 NULL 使用默认属性。count
参数指定了必须到达屏障的线程数。当 count
个线程都到达屏障后,所有线程都将被释放。函数成功时返回 0,否则返回一个错误码。
-
销毁:
pthread_barrier_destroy
函数用于销毁一个屏障:
int pthread_barrier_destroy(pthread_barrier_t *barrier);
这个函数接受一个指向 pthread_barrier_t
类型的指针 barrier
。函数成功时返回 0,否则返回一个错误码。
-
等待:
pthread_barrier_wait
函数用于让线程等待在屏障处:
int pthread_barrier_wait(pthread_barrier_t *barrier);
这个函数接受一个指向 pthread_barrier_t
类型的指针 barrier
。当一个线程调用此函数时,它会进入阻塞状态,直到 count
个线程都到达屏障。一旦 count
个线程都到达屏障,所有线程都将被释放,函数返回 PTHREAD_BARRIER_SERIAL_THREAD
给一个随机选定的线程,其他线程返回 0。
3.例子
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define THREAD_COUNT 4
pthread_barrier_t barrier;
void *threadFunc(void *arg) {
printf("Thread %d waiting at barrier\n", (int)arg);
pthread_barrier_wait(&barrier);
printf("Thread %d passed barrier\n", (int)arg);
return NULL;
}
int main() {
pthread_t threads[THREAD_COUNT];
pthread_barrier_init(&barrier, NULL, THREAD_COUNT);
for (int i = 0; i < THREAD_COUNT; i++)
pthread_create(&threads[i], NULL, threadFunc, (void *)i);
for (int i = 0; i < THREAD_COUNT; i++)
pthread_join(threads[i], NULL);
pthread_barrier_destroy(&barrier);
return 0;
}
这个例子创建了四个线程,每个线程都会等待在屏障处,直到所有线程都到达屏障。一旦所有线程都到达屏障,所有线程都可以继续执行。事实上屏障也可以通过条件变量构造。
需要注意对于线程相关代码的编译需要链接上posix线程库:gcc xxx.c -lpthread -l是链接选项,pthread是库名称,默认不添加空格也可以添加空格gcc xxx.c -l pthread