【博客59】为什么有时候要用条件变量加互斥体而不直接用锁?

内容: 记录下条件变量和锁的区别。最近在和师兄闲聊,跟师兄请教关于服务器的知识的时候,师兄问了下我:知不知道条件变量的作用?为啥要用条件变量,直接用锁不就好了吗?之前在学习的时候有留意过这个问题,这一次师兄再次引发了我对条件变量的思考。

思考一:只用互斥锁,而不用条件变量好不好?

我的思考:不是都好,互斥锁体现的是一种竞争关系,它只能是锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足。两个线程操作同一临界区时,通过互斥锁保护,若A线程已经加锁,B线程再加锁时候会被阻塞,直到A释放锁,B再获得锁运行,进程B必须不停的主动获得锁、检查条件、释放锁、再获得锁、再检查、再释放,一直到满足运行的条件的时候才可以,这将会比较消耗系统的资源的,主要问题在于拿到锁不代表资源就绪,也就是不一定每次拿到锁都是有意义的,而条件变量同样是阻塞,还需要通知才能唤醒,线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,该线程就休眠了,应该仍阻塞在这里,等待条件满足后被唤醒,所以条件变量的特点在于,被唤醒了则代表资源就绪,使每次唤醒都几乎是有意义,说几乎是因为有时候在唤醒到你去拿资源的时候,刚好资源被另一个线程拿走了,这是虚假唤醒的情况。 ,另外锁还有可能造成死锁的情况哦,比如:例如线程A和线程B都需要独占使用2个资源,但是他们都分别先占据了一个资源,然后又相互等待另外一个资源的释放,这样就形成了一个死锁。

示例:只使用互斥锁的情况
【博客59】为什么有时候要用条件变量加互斥体而不直接用锁?
分析:此时线程必须轮询去判断资源是否就绪,每次都要上锁,解锁,耗费cpu。互斥锁单独使用不应该用在生产者与消费者这类模型中。条件变量用于某个线程需要在某种条件成立时才去保护它将要操作的临界区的情况。

思考二:只使用条件变量,不和互斥体一起使用行不行?

这里的解答是我在高性能服务器公众号上的一篇文章学到的,以前还没考虑过不配合使用的情况,这里引用下文章中的一段解释。

假设条件变量不与互斥体对象结合的效果:
【博客59】为什么有时候要用条件变量加互斥体而不直接用锁?

思考三:条件变量唤醒后就一定可以是资源就绪吗?

我的思考:这个不是都可以,会有虚假唤醒的情况哦,因为当条件变量满足时,线程被唤醒,然后去操作资源,因为刚才唤醒时是满足的,但现在可能已经不满足了,所以你被唤醒后需要再判断是否满足,这也意味着条件唤醒的逻辑代码要使用while(1)而不是if。然后使用条件变量的唤醒,要使用单唤醒,不能一直是广播唤醒,这样在epoll+线程池模型中就会出现惊群。然后以前学习的时候,看资料说,单唤醒是唤醒一个至多个线程,但是我实际测试,总是唤醒一个。经师兄点拨,新版linux内核已经解决掉这个问题了,线程会有优先级和权重的。

总结:锁体现的是一种竞争关系,而条件变量体现的是一种资源请求关系。以上这是我对这个问题的思考哈,有不对的地方,欢迎前辈指正,我定虚心求教。

大三学生一枚,文章均非抄袭或者模仿,均为原创,仅代表个人观点,如果文章有错误的地方,欢迎在下方提出,每条评论我都会去认真看并回复。