POSIX 线程屏障
我们一定有组团外出参观旅游景点的经历,假设我们有五个同伴,其中一个同伴 A 去售票处购买团体票,其余四个同伴只能在验票处等待(因为还没买到票),待同伴 A 买到团体票并到入口验票处集合时,我们才可以通过验票处进入旅游景点。
在计算机领域,存在着许多相似的应用场景:对超大数组进行排序,为了发挥多核处理器的并发性能,可以使用 10 个线程分别对这个超大数组的 10 个部分进行排序。必须要等这 10 个线程都完成了各自的排序后,才能进行后续的归并操作。先完成的线程会挂起等待,直到所有线程都完成后,才唤醒所有等待的线程。
以上应用场景我们可以使用信号量、条件变量等线程间通信方法完成,但为了更为优雅简洁地实现,POSIX 标准定义了线程屏障及其操作。
线程屏障 Barrier 又名线程栅栏,它主要用于协调多个线程并行共同完成某项任务。一个线程屏障对象可以使得每个线程阻塞,直到所有协同(合作完成某项任务)的线程执行到某个指定的点,才让这些线程继续执行(这犹如储存水的大坝)。
POSIX 线程屏障的类型为 pthread_barrier_t。使用时需要定义一个 pthread_barrier_t 类型的变量,如:
pthread_barrier_t barrier;
一个 POSIX 线程屏障必须要调用 pthread_barrier_init 函数初始化之后才可以使用。
如果需要等待一个线程屏障,可以调用 pthread_barrier_wait 函数,中断服务程序不可以调用任何 POSIX 线程屏障 API。
当一个线程屏障使用完毕后(并确保以后也不再使用),应该调用 pthread_barrier_destroy 函数将其删除,SylixOS 会回收该线程屏障占用的内核资源。
创建一个 POSIX 线程屏障需要使用 POSIX 线程屏障属性块。POSIX 线程屏障属性块的类型为 pthread_barrierattr_t。使用时需要定义一个 pthread_barrierattr_t 类型的变量,如:
pthread_barrierattr_t barrierattr;
线程屏障属性块
线程屏障属性块的初始化和删除
#include <pthread.h>
int pthread_barrierattr_init(pthread_barrierattr_t *pbarrierattr);
int pthread_barrierattr_destroy(pthread_barrierattr_t *pbarrierattr);
以上两个函数原型分析:
- 函数成功时返回 0,失败时返回错误号。
- 参数 pbarrierattr 是 POSIX 线程屏障属性块的指针。
设置和获取线程屏障属性块的进程共享属性
#include <pthread.h>
int pthread_barrierattr_setpshared(pthread_barrierattr_t *pbarrierattr,
int shared);
函数 pthread_barrierattr_setpshared 原型分析:
- 此函数返回 0。
- 参数 pbarrierattr 是 POSIX 线程屏障属性块的指针。
- 参数 shared 标识了 POSIX 线程屏障属性块是否进程共享。
#include <pthread.h>
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *pbarrierattr,
int *pshared);
函数 pthread_barrierattr_getpshared 原型分析:
- 此函数返回 0。
- 参数 pbarrierattr 是 POSIX 线程屏障属性块的指针。
- 输出参数 pshared 标识了 POSIX 线程屏障属性块是否进程共享。
线程屏障
线程屏障的初始化和删除
#include <pthread.h>
int pthread_barrier_init(pthread_barrier_t *pbarrier,
const pthread_barrierattr_t *pbarrierattr,
unsigned int count);
函数 pthread_barrier_init 原型分析:
- 此函数成功时返回 0,失败时返回错误号。
- 参数 pbarrier 是 POSIX 线程屏障的指针。
- 参数 pbarrierattr 是 POSIX 线程屏障属性块的指针,可以为 NULL。
- 参数 count 标识了 POSIX 线程屏障将屏障多少个线程。
#include <pthread.h>
int pthread_barrier_destroy(pthread_barrier_t *pbarrier);
函数 pthread_barrier_destroy 原型分析:
- 此函数成功时返回 0,失败时返回错误号。
- 参数 pbarrier 是 POSIX 线程屏障的指针。
线程屏障的等待
#include <pthread.h>
int pthread_barrier_wait(pthread_barrier_t *pbarrier);
函数 pthread_barrier_wait 原型分析:
- 此函数成功时返回 0,失败时返回错误号。
- 参数 pbarrier 是 POSIX 线程屏障的指针。
下面程序展示了 POSIX 线程屏障的使用,程序创建四个线程和一个 POSIX 线程屏障;四个线程延时不同的秒数,然后等待线程屏障,待四个线程都到达线程屏障时,四个线程继续运行。
#include <stdio.h>
#include <pthread.h>
#define TEST_THREAD_NR 4
static pthread_barrier_t barrier;
static void *thread_test (void *arg)
{
int sec = (int)arg;
printf("thread_test(): i will sleep %d second.\n", sec);
sleep(sec);
pthread_barrier_wait(&barrier);
printf("thread_test(): i am here.\n");
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_t tid[TEST_THREAD_NR];
pthread_attr_t attr;
int ret;
int i;
ret = pthread_barrier_init(&barrier, NULL, TEST_THREAD_NR);
if (ret != 0) {
fprintf(stderr, "barrier create failed.\n");
return (-1);
}
ret = pthread_attr_init(&attr);
if (ret != 0) {
fprintf(stderr, "pthread attr init failed.\n");
return (-1);
}
for (i = 0; i < TEST_THREAD_NR; i++) {
ret = pthread_create(&tid[i], &attr, thread_test, (void *)(i + 1));
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
}
for (i = 0; i < TEST_THREAD_NR; i++) {
pthread_join(tid[i], NULL);
}
pthread_barrier_destroy(&barrier);
return (0);
}
在 SylixOS Shell 下运行程序,结果如下:
# ./POXIS_Thread_Barrier
thread_test(): i will sleep 1 second
thread_test(): i will sleep 2 second
thread_test(): i will sleep 3 second
thread_test(): i will sleep 4 second
thread_test(): i am here
thread_test(): i am here
thread_test(): i am here
thread_test(): i am here
通过程序的运行结果可以看出,所有线程创建成功之后,才会同时运行到“i am here”打印处,因此可以得出,线程屏障可以使不同的线程运行到同一点上,再继续运行(尽管线程的创建时间不同)。