对线程条件变量的理解

对线程中条件变量的理解:

         最近在学线程,学到这个条件变量的时候,感觉很难理解。。。查阅了大量的资料,终于有所突破,现在将一些体会写下来:

        不得不提的是,条件变量必须牵涉到互斥锁,这个具体为什么,在我转载的另一篇文章中介绍到,有兴趣的可以了解一下。。。

        首先,先简单的介绍一下条件变量,条件变量是用来通知共享数据状态信息的。可以使用条件变量来通知队列已空,或非空。为什么在线程中要引用条件变量呢?我在这里举个例子:线程A用来向共享数据-队列写入数据,而线程B用来读取队列中的数据。当然了,这个地方共享数据需要用一个互斥量保护。在线程B读取之前,需要锁住互斥量,然后判断队列是否为空,如果非空,直接读取然后返回就OK了;如果是空呢?那么线程B就需要阻塞,直到线程A向队列中写入数据使队列非空,如果这样的话,线程B的生命周期就没有什么意义了,他用他的一生来阻塞等待。。。为什么?因为线程B在判断之前,已经锁住了互斥量,这个时候线程A哪怕有很多的数据要写入队列,但是,他没有竞争到这个锁,就不得不等待线程B释放这个锁。。。就相当于B拿着大门的钥匙在屋里边等着A回家一样。这样就悲剧了。。。不是吗?所以,线程引入了条件变量这个概念。。。

       先打一个比方,有两个人A,B有两个保险柜a,b(a保险柜属于A,b保险柜属于B),意外的是,这两个保险柜公用一个钥匙,而且A,B两人只有一把钥匙。。。保险柜里面各自锁了自己的手机。现在有这样的一件事:A想要给B发一条短信,而B呢?B想要查看A发给自己的短信内容。。。这两件事都离不开手机,所以他们不得不去竞争这唯一的钥匙。。。结果又两种:1,A抢到了这把钥匙,打开a保险柜,取出手机给B了一个短信,然后将钥匙给刚才没有抢到钥匙的B,B打开b保险柜,取出手机看到了A的短信;2,这第二种结果就有意思了,假设B抢到了这把钥匙,打开b保险柜,取出手机,但是却没有看到短信,B怎么办呢?很明智的,B重新锁住了保险柜,然后将钥匙又给了A,对A说,你先用吧,我等着你。。。A打开a保险柜,给B的手机发了一个短信,然后把钥匙给了B,对B说,我忙完了,你不用等待了,你继续。。。然后呢,B就打开保险柜取出手机看到了短信。。。。

        很温馨的第二种结果,哈哈,其实呢,这就是条件变量的机制(我个人理解,欢迎高手提出错误)。

       两个人相当于两个线程,而唯一的钥匙呢,就是互斥锁吧。。。第二种结果的整个过程在pthread_cond_wait(),pthread_cond_signal()的实现过程中表现的淋漓尽致。。。这个函数的作用会阻塞需要读取队列的线程B,但是呢,在阻塞之前,条件变量操作将会解锁互斥量(这时线程A在等待线程B释放这个锁,所以此时线程A会竞争到锁然后向队列中写入数据,写入之后,线程A会向线程B发送信号,唤醒线程B,同时解锁这个互斥量),而在线程B被唤醒时,会再次锁住互斥量。。。这里有一段代码,可以帮助你更好的理解条件变量的机制:

#include"stdio.h"
#include"stdlib.h"
#include"error.h"
#include"string.h"
#include"errno.h"
#include"pthread.h"
#include"time.h"

typedef struct my_struct_tag
{
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int value;
}my_struct_t;


my_struct_t data = {
    PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};

int hibernation = 1;


void *wait_thread(void *arg)
{
    int status;
    sleep(hibernation);//刻意的想要实现第二种结果,即让main抢到这个锁

    status = pthread_mutex_lock(&data.mutex);//锁住互斥量,
    if(status)
    {
        fprintf(stderr, "Lock mutex error: %s\n", strerror(errno));
        pthread_exit(NULL);
    }
    

    data.value = 1;//这个操作就是这个线程需要完成的任务
    

    status = pthread_cond_signal(&data.cond);//完成任务后,唤醒等待cond条件变量的线程,注意,这个函数一次最多只能唤醒一个线程,(如果多个线程在等待,会按照      优先级进行线程排序’如果需要同时唤醒多个线程,需要用到pthread_cond_broadcast()函数。

    if(status)
    {
        fprintf(stderr, "Signal error: %s\n", strerror(errno));
        pthread_exit(NULL);
    }


    status = pthread_mutex_unlock(&data.mutex);
    if(status)
    {
        fprintf(stderr, "Unlock mutex error: %s\n", strerror(errno));
        pthread_exit(NULL);
    }

    return NULL;
}



int main(int argc, char *argv[])
{
    int status;
    pthread_t wait_thread_id;
    struct timespec timeout;

    if(argc > 1)
    {
        hibernation = atoi(argv[1]);//默认值为1,你可以输入参数修改默认值
    }


    status = pthread_create(&wait_thread_id, NULL, wait_thread, NULL);//创建wait_thread线程
    if(status)
    {
        fprintf(stderr, "Create thread error: %s\n", strerror(errno));
        return -1;
    }


    timeout.tv_sec = time(NULL) + 2;
    timeout.tv_nsec = 0;


    status = pthread_mutex_lock(&data.mutex);
    if(status)
    {
        fprintf(stderr, "Main thread_mutex_lock error: %s\n", strerror(errno));
        return -1;
    }

    while(data.value == 0)
    {
        status = pthread_cond_timedwait(&data.cond, &data.mutex, &timeout);//这个是时间等待函数,在这个时间内等待;pthread_cond_wait()是不需要等待时间的,这个函数会首先解锁mutex,然后阻塞,等待线程wait_thread发送信号来唤醒他。在唤醒后,这个线程会重新锁着mutex这个互斥量。在这里还有一个问题,如果等到条件变量的等待被唤醒,他需要第一时间加锁互斥量,但是,发送信号的线程并没有释放互斥量,该怎么办呢?这个刚被唤醒的等待需要再次等待,这样就切换了两次环境。所以,尽量保证发信号时就解锁相应的互斥量。
        if(status == ETIMEDOUT)
        {
            printf("Condition wait timed out.\n");
            break;
        }
        else if(status != 0)
        {
            fprintf(stderr, "Wait on condition error: %s\n", strerror(errno));
            exit(1);
        }
    }


    if(data.value != 0)//如果value的值发生改变,就说明线程已经修改了value的值,并且发送了信号来唤醒等待cond条件变量的线程。
    {
        printf("Conditon was signaled.\n");
    }
    status = pthread_mutex_unlock(&data.mutex);
    if(status != 0)
    {
        fprintf(stderr, "Pthread_mutex_unlock error: %s\n", strerror(errno));
        exit(1);
    }

    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值