條件變量的作用是用于多線程之間關于共享資料狀态變化的通信。當一個動作需要另外一個動作完成時才能進行,即:當一個線程的行為依賴于另外一個線程對共享資料狀态的改變時,這時候就可以使用條件變量
假設沒有條件變量,對于一個生産者消費者問題,消費線程在得知隊列中沒有産品時,将阻塞自己。生産線程給隊列中放入産品,但是沒有辦法激活消費線程,而消費線程處于阻塞狀态也沒有辦法自激活。如果消費線程使用忙等的方式,通過不斷地查詢來判斷是否有産品将大量的浪費CPU時間。消費線程可以使用睡眠+查詢的方式,即發現隊列中沒有産品時,sleep一段時間,然後再查詢。問題是睡眠多長時間?時間太長,實時性不好,時間太短,還是浪費CPU時間。
是以,通過生産線程通過喚醒消費線程時最好的方式。現在我們考慮一種實作,消費線程在阻塞之前要先解鎖,同時還要将自己的辨別符放入一個地方,以便生産線程通過這個辨別符來激活自己。這樣看起來是沒問題了,然而不要忘記了,線程之間是并發/并行的。消費線程可能剛完成解鎖的操作,就被生産線程擷取到了并開始執行,這時,因為消費線程還未挂起自己,來不及将自己的辨別符儲存在某個位置,是以生産線程不認為有正在等待的線程。這時,切換到消費線程後,消費線程将永遠的等待下去,雖然隊列中有産品。而生産線程因為隊列中有産品可能也一直的等待下去,形成了死鎖。
解決方法是必須讓解鎖、儲存線程辨別符、挂起這一系列操作成為原子操作。這中解決方案就是條件變量,是以不難想到使用條件變量的時候必須要“伴随”一個互斥量。
條件變量是與互斥量相關聯的一種用于多線程之間關于共享資料狀态改變的通信機制。它将解鎖和挂起封裝成為原子操作。等待一個條件變量時,會解開與該條件變量相關的鎖,是以,使用條件變量等待的前提之一就是保證互斥量加鎖。線程醒來之後,該互斥量會被自動加鎖,是以,在完成相關操作之後需要解鎖。
條件變量總是和謂語相關,學過《離散數學》應該還記得謂語表達式吧。謂語是具有唯一真假值的句子。程式中,可以用謂語來描述目前線程需要的狀态。如果該謂語值為假,需要使用條件變量等待。醒來之後,由于系統的并發性,一般需要再次判斷謂語值是否為真,如果不為真,則再次使用條件變量進行等待。
互斥量是用來防止對不變量的破壞,換句話說,是用來規範線程對共享資料的競争使用。而條件變量是用來對線程同步,即用來協調各個線程合作完成某個任務。比如:足球場上,兩個足球隊對一個球的使用叫做競争,可以使用“馬賽回旋”這種“互斥量”對球加鎖,防止被搶。而傳球這個動作就是使用“條件變量”進行喚醒,它的作用是保證一個球隊的各個成員能協作起來将球踢進對方的球門。
條件變量的關注對象是共享資料狀态的變化,這一變化可以使用謂語來描述。因為涉及到共享資料,是以需要互斥量。互斥量和條件變量的對應關系為1:N.就是說一個互斥量可以對應多個條件變量,一個條件變量隻能對應一個互斥量。這個可以這樣了解:因為共享資料有很多種狀态,描述這些狀态就需要多個謂語,是以需要用多個條件變量。
條件變量和謂語的對應關系一般最好為1:1.1:N或者N:1并不是不可以,但是容易引起死鎖和競争問題,要特别注意。若1個條件變量對應于多個謂語時,喚醒應采用廣播的方式而不是signal的方式。
本文轉自hipercomer 51CTO部落格,原文連結:http://blog.51cto.com/hipercomer/914841