POSIX 线程屏障

更新时间:
2024-03-14
下载文档

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”打印处,因此可以得出,线程屏障可以使不同的线程运行到同一点上,再继续运行(尽管线程的创建时间不同)。

文档内容是否对您有所帮助?
有帮助
没帮助