使用條件變量時爲啥一定要指定一個鎖?

今天看代碼的時候突然發現了這個問題。
條件變量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掛起的一個/幾個線程。這樣的工程問題中可能更容易理解。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章