多线程编程-互斥锁/条件变量/关卡的使用实例

序言

之前的文章已经回顾了多线程同步的6种方式,这里就互斥锁和条件变量的配合使用,以及关卡的使用举个栗子。

以牛客网上迅雷的一道面试题为例。


1. 题目
编写一个程序,开启3个线程,这3个线程的ID分别为A、B、C。

每个线程将自己的ID在屏幕上打印10遍,要求输出结果必须按ABC的顺序显示;如:ABCABC….依次递推。


2. 实现思路
  • [1] 不能只使用互斥锁,因为互斥锁的特点是资源的”无序访问“

  • [2] 可以使用信号量,三个线程之间形成依次通知的关系,由主线程通知启动。

  • [3] 使用条件变量,三个线程之间形成相互等待的关系,可用条件变量通知等待的线程,由主线程通知启动。

    这里使用条件变量进行实现。


3. 一些线程函数原型


  • 线程创建函数:int pthread_create (pthread_t *tid, const pthread_attr_t * attr, void * (void *), void *arg);

  • 等待线程结束函数:int pthread_join(pthread_t tid, void **ret);


  • 互斥锁初始化函数:int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);

  • 互斥锁上锁:int pthread_mutex_lock (pthread_mutex_t *mutex);


  • 条件变量初始化函数: int pthread_cond_init(pthread_cond_t *condition, pthread_condattr_t *condattr);

  • 条件变量等待函数:int pthread_cond_wait(pthread_cond_t *condition, pthread_mutex_t *mutex);

  • 条件变量定时等待函数:int pthread_cond_timedwait(pthread_cond_t *condition, pthread_mutex_t *mutex, const struct timespec *abstime);

  • 条件变量通知函数:int pthread_cond_signal(pthread_cond_t *cond);

  • 条件变量广播函数:int pthread_cond_broadcast(pthread_cond_t *cond);


  • 关卡初始化函数:int pthread_barrier_init( pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count);

  • 关卡等待函数:int pthread_barrier_wait( pthread_barrier_t *barrier);


4. 代码实现 - 使用pthread_join()
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t A, B, C;                                      //线程ID
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;       //互斥锁,条件变量对资源的互斥访问
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;    //条件变量

int flag = 0;                                           //把线程阻塞在条件变量的条件,全局变量
void *printThread1(void *arg);
void *printThread2(void *arg);
void *printThread3(void *arg);


int main()
{
    /*动态初始化方式
    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&condition, NULL);
    */

    //建立三个子线程
    int res;
    if ((res = pthread_create(&A, NULL, printThread1, NULL)) != 0)
    {
        printf("线程A创建失败\n");
        exit(-1);
    }
    //条件置位让线程1开始执行
    flag = 1;
    if ((res = pthread_create(&B, NULL, printThread2, NULL)) != 0)
    {
        printf("线程B创建失败\n");
        exit(-1);
    }
    if ((res = pthread_create(&C, NULL, printThread3, NULL)) != 0)
    {
        printf("线程C创建失败\n");
        exit(-1);
    }

    //等待各个子线程结束
    pthread_join(A, NULL);
    pthread_join(B, NULL);
    pthread_join(C, NULL);

    //释放内存之前销毁互斥锁和条件变量
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&condition);
    return 0;
}

void *printThread1(void *arg)
{
    int i = 0;
    while (i < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 1)                              //flag == 1时,线程1执行
            pthread_cond_wait(&condition, &lock);

        printf("A");
        flag = 2;
        i++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    return NULL;            //或return ((void *)0);
}

void *printThread2(void *arg)
{
    int j = 0;
    while (j < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 2)                              //flag == 2时,线程2执行
            pthread_cond_wait(&condition, &lock);

        printf("B");
        flag = 3;
        j++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}


void *printThread3(void *arg)
{
    int k = 0;
    while (k < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 3)                              //flag == 3时,线程3执行
            pthread_cond_wait(&condition, &lock);

        printf("C");
        flag = 1;
        k++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

说明:

[1] 互斥锁和条件变量静态创建,因此可以选择静态初始化(赋值)或者动态初始化(函数)的方式

[2] 之前将条件置位语句flag = 1放在了创建完线程之后,发现有的时候能正常运行输出,有的时候又完全没有反应。问题在于线程创建之后,运行哪一个是随机的,如果不是线程1条件在等待,则不会继续运行。

[3] pthread_join()对不同函数的等待没有先后顺序,所以pthread_join(A, NULL),pthread_join(B, NULL),pthread_join(C, NULL)谁在前谁在后都没有关系。

[4] int i, j, k的定义放到了各个线程中而不是作为全局变量,因为线程运行起来后直至运行结束才会退出,因此可作为局部变量。


4. 代码实现 - 使用关卡Barriers
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

pthread_t A, B, C;                                      //线程ID
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;       //互斥锁,条件变量对资源的互斥访问
pthread_cond_t condition = PTHREAD_COND_INITIALIZER;    //条件变量
pthread_barrier_t barrier;                              //关卡

int flag = 0;                                           //把线程阻塞在条件变量的条件,全局变量
void *printThread1(void *arg);
void *printThread2(void *arg);
void *printThread3(void *arg);


int main()
{
    //关卡初始化,count = 子线程个数 + 1(主线程)
    pthread_barrier_init(&barrier, NULL, 4);

    //建立三个子线程
    int res;
    if ((res = pthread_create(&A, NULL, printThread1, NULL)) != 0)
    {
        printf("线程A创建失败\n");
        exit(-1);
    }
    //条件置位让线程1开始执行
    flag = 1;
    if ((res = pthread_create(&B, NULL, printThread2, NULL)) != 0)
    {
        printf("线程B创建失败\n");
        exit(-1);
    }
    if ((res = pthread_create(&C, NULL, printThread3, NULL)) != 0)
    {
        printf("线程C创建失败\n");
        exit(-1);
    }

    //设置线程关卡,每个线程运行到关卡均等待
    pthread_barrier_wait(&barrier);

    //释放内存之前销毁互斥锁和条件变量
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&condition);
    pthread_barrier_destroy(&barrier);
    return 0;
}

void *printThread1(void *arg)
{
    int i = 0;
    while (i < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 1)
            pthread_cond_wait(&condition, &lock);

        printf("A");
        flag = 2;
        i++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_barrier_wait(&barrier);
    return NULL;
}

void *printThread2(void *arg)
{
    int j = 0;
    while (j < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 2)
            pthread_cond_wait(&condition, &lock);

        printf("B");
        flag = 3;
        j++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_barrier_wait(&barrier);
    return NULL;
}


void *printThread3(void *arg)
{
    int k = 0;
    while (k < 10)
    {
        pthread_mutex_lock(&lock);
        if (flag != 3)
            pthread_cond_wait(&condition, &lock);

        printf("C");
        flag = 1;
        k++;
        pthread_cond_signal(&condition);
        pthread_mutex_unlock(&lock);
    }
    pthread_barrier_wait(&barrier);
    return NULL;
}

pthread_cond_wait()函数说明:

[1] 抢占到互斥锁并运行到条件if (flag != 1)之后,线程阻塞在条件变量

[2] pthread_cond_wait()内部会解锁,然后等待条件变量被其他线程激活

[3] 被激活后会再自动加锁(在其他线程释放互斥锁之后)

[4] 并重新判断条件变量阻塞条件是否成立,成立重新挂起,不成立则继续往下执行



Acknowledgements:
http://blog.csdn.net/ithomer/article/details/6031723
http://blog.csdn.net/freedom8531/article/details/45419149

2017.08.31

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值