Linux应用开发:浅谈为什么条件变量要和互斥锁配合使用


前言

linux系统下的线程同步组件有很多,例如互斥锁、自旋锁、信号量、条件变量等等。使用条件变量进行线程同步时我们注意到,条件变量等待函数的调用方式为:pthread_cond_wait(&cond, &mutex)。等待函数的参数中附带了一个互斥锁,那么这个互斥锁的作用是什么呢?


一、什么是互斥锁?

互斥锁在多线程编程场景中比较常用,尤其是在多个线程需要同时访问同一变量时。它的主要作用是确保在任何给定时刻,仅有一个线程能够访问该共享变量,从而避免数据竞争和不一致性问题。当线程尝试访问共享资源前,它必须先请求获得互斥锁。一旦某个线程成功获取了锁,其他所有试图访问该共享变量的线程都将被阻塞在锁的申请过程中,直至锁被当前持有者释放。这种机制有效地保护了共享变量,确保了数据的一致性和完整性。

二、什么是条件变量

条件变量也是多线程编程场景中常用的同步手段,它可以线程之间按照预定的顺序执行。例如,在需要线程1完成其特定任务之后,才让线程2开始执行其工作的情况下,条件变量就能发挥关键作用。通过条件变量,可以等待某个条件成立(如线程1完成工作)后,再通知其他线程(如线程2)继续执行,从而实现线程间的有序协作。

三、为什么条件变量需要互斥锁配合

按照如上关于条件变量的介绍,假设某一业务场景,线程1等待条件变量时产生阻塞,线程2完成对应操作后,通知线程1解除条件变量阻塞。仿佛不使用互斥锁也可以,那么为什么条件变量的等待函数pthread_cond_wait一定要加入一个互斥锁参数呢?

我们引入如下场景:
对于使用条件变量的线程1而言,它的执行过程可划分为以下2步:
1.已申请到条件变量,正在执行操作,执行耗时大约10ms
2.执行完任务后,线程1调用pthread_cond_wait重新等待条件变量

仿佛运行很正常,但其中涉及到了一个隐患,即如果线程1正在执行任务,执行到了第3ms期间线程2又想让任务1执行一次,调用了pthread_cond_signal函数,这时线程1还没准备开始等待,就会导致这个信号丢失。结果:线程2想让线程1执行多1次,而线程1没有接收到这个信号,没有执行第2次任务。

而如果条件变量引入了互斥锁,就可以很好地解决上述问题,线程2在发送信号前,需要申请互斥锁,避免了线程1执行期间漏信号的情况。使用效果如下例所示:

In Thread1:
 
pthread_mutex_lock(&m_mutex);   
pthread_cond_wait(&m_cond,&m_mutex);   
pthread_mutex_unlock(&m_mutex);  
 
 
 
In Thread2:
 
pthread_mutex_lock(&m_mutex);   
pthread_cond_signal(&m_cond);   
pthread_mutex_unlock(&m_mutex);  
 

条件变量还有个特性,某个线程正在等待条件变量时,会释放手上拥有的锁资源。线程1申请到锁后,开始等待条件变量,随后函数内会自动释放拥有的锁,避免出现线程2拿不到锁,pthread_cond_signal无法执行的bug。

总结

对于很多业务场景而言,条件变量更多是用于提醒某个线程处理临界资源的。
例如,对于生产者-消费者模型。生产者线程和消费者线程有多个,而生产者和消费者之间存在一个货架队列,这些线程都想要在这个队列上放置货物、拿走货物。这时就需要互斥锁对临界资源(队列)进行保护,避免修改队列长度时多个线程窜入访问。
这种业务场景,我们需要条件变量和互斥锁配合使用。当货架为空时,消费者都阻塞在条件变量,若生产者将货物放在了货架上,则生产者通知消费者来拿货,消费者的条件变量得以解除阻塞,但访问队列期间,需要互斥锁对队列长度加以保护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值