关于linux条件变量的一点思考

Posted analogous_love

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于linux条件变量的一点思考相关的知识,希望对你有一定的参考价值。

设想有这样一种应用场景:  

有A、B两个线程同时递增一个整型变量v,线程C在变量v是3的倍数时,输入v的值。因为涉及到多个线程同时读写同一个变量,所以肯定需要使用互斥体mutex对变量v进行保护,即同一时刻只能有且只有一个线程对v进行修改。假设A、B、C三个线程得到cpu时间片几率相等,如果不使用条件变量的话,在线程C中只能采取轮询的方式不断地去检测变量v的值是否是3的倍数。这样存在以下问题——碰巧的情况下,可能v的值的未发生改变时会被线程C检测多次。这样其实是在做无用功。  

如果使用条件变量的话,当A或者B改变了v的值时,再去通知C去检测v的值是否是3的倍数,这样可以省去C的很多次的无用检测。在C不检测的情况下,C线程以休眠状态挂起。  

代码如下:

#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>  
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>

int index = 1;

pthread_mutex_t lock;
pthread_cond_t cond;

void* fun1(void* p)

    while (index < 50)
    
        pthread_mutex_lock(&lock);    
        index++;
        printf("In fun1 : %d\\n",index);
        pthread_cond_signal(&cond);//当有变化后,使用signal通知wait函数
        pthread_mutex_unlock(&lock);
        usleep(0.1);
    


void* fun2(void* p)

    while (index < 50)
           
        pthread_mutex_lock(&lock);    
        index++;
        printf("In fun2 : %d\\n",index);
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
        usleep(0.1);
    


void* fun3(void*)

    int i=0;
    while (i < 70)
    
         pthread_mutex_lock(&lock);
         while ( index % 3 != 0)
         
             pthread_cond_wait(&cond, &lock);//如果获得了互斥锁,但是条件不合适的话,wait会释放锁,不往下执行。当变化后,条件合适,将直接获得锁。
         

         printf("%d\\n",index/3);

         i++;
         pthread_mutex_unlock(&lock);
         usleep(0.1);
    


int main()

    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_t tid1,tid2,tid3;
    pthread_create(&tid1, NULL, fun1, NULL);
    pthread_create(&tid2, NULL, fun2, NULL);
    pthread_create(&tid3, NULL, fun3, NULL);

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_join(tid3, NULL);

    return 0;


程序中,每当线程1或线程2改变了全局变量index的值,就通知线程3去检测线程index的值是否是3的倍数。需要注意的是:

线程1或2改变了index值,去通知线程3去检测index值,这里只是通知线程3,不代表线程3一定会得到cpu时间片。所以可能会出现即使index是3的倍数时,由于线程3没有得到cpu时间片而不执行,也就是错过打印index值的机会(即使这个时候index是3的倍数)。如下图:



图中标红的地方,index的值都是3的倍数,由于线程3没有机会执行,所以并没有打印出来。


所以需要注意的地方是:使用条件变量时,虽然可以在条件满足时通知相关等待的线程,但等待的线程不一定会在条件满足时执行需要的动作,这是个几率问题。另外的,条件变量使用需要理解如下内容:


1.调用pthread_cond_wait或std::condition_variable.wait()(C11新增API)时会对相应的mutex进行解锁,然后线程睡眠等待条件触发。
2.当其他线程调用pthread_cond_signal/pthread_cond_broadcast 或 std::condition_variable.notify_one()/notify_all()(C11新增API)时,pthread_cond_wait或std::condition_variable.wait()所在的线程可能会苏醒过来,如果苏醒过来执行的话,pthread_cond_wait或std::condition_variable.wait()会对相应的mutex加锁。

产生条件变量的初衷是应对如下需求:
对某个共享变量进行检测看是否满足条件,避免了:
1.读取共享数据频繁的加锁解锁;
2.减少判断条件不满足的尝试的次数。 

题外话

   上述程序存在两个问题:

1. 线程3在线程1和2退出以后,挂起,导致整个进程无法正常退出。如何解决?

2. 如何让线程3打印出所有index是3的倍数的值?


参考:http://www.cnblogs.com/wzben/p/5431071.html

以上是关于关于linux条件变量的一点思考的主要内容,如果未能解决你的问题,请参考以下文章

关于样本方差以及样本协方差的一点思考

Think in Speed (关于速度的一点思考)

20170905:关于投资我的一点思考

关于敏捷开发本质的一点思考

关于容器化以及 k8s 的一点个人思考

关于Java泛型"擦除"的一点思考