使用条件变量时为啥一定要指定一个锁?

来源:互联网 发布:淘宝提前收款条件 编辑:程序博客网 时间:2024/06/06 21:05

今天看代码的时候突然发现了这个问题。
条件变量wait的时候必须指定一个已经get到了的锁。
去网上搜了一下发现至少是中文范围内,全网都不知道为什么。很多人问“条件变量为什么要用互斥锁来保护?”,实际上那个锁才不是用来保护条件变量的。

后来我想了一下,应该是这样的。

先看使用情景。
以盖小区为例。
a负责盖房子,x负责装窗子,y负责粉刷,z负责安装电路。
所以4个人做事时,房子所在的土地就是被竞争的资源。

我们来看看小区怎么个盖法。
肯定要a先工作,xyz等待a完工才能一个一个进去施工。不然涂料有毒,窗子和电线进去会毒死;窗子很大,涂料和电线进去耽误窗子安装;电线也是,涂料和窗子进去可能会被电到。但是谁先谁后无所谓。

这种情况下只用锁是不行的。因为如果xyz先get到锁,土地上房子还没盖,怎么装修?
那么先让a盖房,再让xyz用锁争夺先后呢?问题我们不是就盖一个房子,我们要盖一个小区呢,a什么时候回来啊?所以只用锁是不行的。

这时候就要条件变量出场了。
xyz一上来都需要get锁,然后wait条件变量。条件变量退出之后就可以干活儿,然后释放锁。
a跟他们不一样。a get到锁之后就直接开始干活,干完了改变条件变量,使xyz可以竞争锁。
xyz完成工作后,a又可以开始工作。。。

所以如果axyz4个线程同时开始工作,如果x一上来就get到了锁,axyz就会锁在一起。因为a也需要get到锁。aget不到锁就不会改变条件变量,xyz就不能工作。大家就都在等待对方,永远等下去。
而且就算a第一个get到了锁,如果是只唤醒一个的情况,唤醒的恰好不是get到锁的那个,还是会死锁。

所以wait函数一定需要你把锁传给它,它替你释放锁

这样,即使xyz先拿到了锁,也会在wait阶段被强行释放掉,不影响a的工作。等a工作完了,会改变条件变量,wait函数不再阻塞,但是退出前还会帮xyz get一次锁,保证互斥访问。
唤醒一个的情况下,比如唤醒了x,但是x没有锁,yget到了锁。y执行到wait语句时也会放开锁,这样x就可以最终拿到锁,开始执行。

就是这样。

至于a怎么知道xyz工作做完了,可以用标志位。隔一段时间查询一次就行了。
还有就是为什么不能不用锁。不用锁怎么管理xyz对房子的访问呢?wait函数退出了条件变量就不管事了。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
其实对于条件变量的wait操作,更应该当做是线程挂起。notify相当于唤醒被wait挂起的一个/几个线程。这样的工程问题中可能更容易理解。

1 0