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

本文介绍了一个使用条件变量来协调多线程工作的例子。通过线程A、B递增全局变量,线程C在该变量为3的倍数时打印其值。文章探讨了条件变量的原理,包括如何在条件满足时唤醒等待线程,并讨论了潜在的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

有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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值