1. 臨界區
對臨界資源進行訪問的那段代碼稱為臨界區。
為了互斥訪問臨界資源,每個進程在進入臨界區之前,需要先進行檢查。
2. 同步與互斥
同步:多個進程因為合作產生的直接制約關系,使得進程有一定的先后執行關系。
互斥:多個進程在同一時刻只有一個進程能進入臨界區。
3. 信號量
信號量(Semaphore)是一個整型變量,可以對其執行 down 和 up 操作,也就是常見的 P 和 V 操作。
down : 如果信號量大於 0 ,執行 -1 操作;如果信號量等於 0,進程睡眠,等待信號量大於 0;
up :對信號量執行 +1 操作,喚醒睡眠的進程讓其完成 down 操作。
down 和 up 操作需要被設計成原語,不可分割,通常的做法是在執行這些操作的時候屏蔽中斷。
如果信號量的取值只能為 0 或者 1,那么就成為了 互斥量(Mutex) ,0 表示臨界區已經加鎖,1 表示臨界區解鎖。
使用信號量實現生產者-消費者問題
問題描述:使用一個緩沖區來保存物品,只有緩沖區沒有滿,生產者才可以放入物品;只有緩沖區不為 空,消費者才可以拿走物品。
因為緩沖區屬於臨界資源,因此需要使用一個互斥量 mutex 來控制對緩沖區的互斥訪問。
為了同步生產者和消費者的行為,需要記錄緩沖區中物品的數量。數量可以使用信號量來進行統計,這 里需要使用兩個信號量:empty 記錄空緩沖區的數量,full 記錄滿緩沖區的數量。
其中,empty 信號量是在生產者進程中使用,當 empty 不為 0 時,生產者才可以放入物品;full 信號量 是在消費者進程中使用,當 full 信號量不為 0 時,消費者才可以取走物品。
注意,不能先對緩沖區進行加鎖,再測試信號量。也就是說,不能先執行 down(mutex) 再執行 down(empty)。如果這么做了,那么可能會出現這種情況:生產者對緩沖區加鎖后,執行 down(empty) 操 作,發現 empty = 0,此時生產者睡眠。
消費者不能進入臨界區,因為生產者對緩沖區加鎖了,消費者就無法執行 up(empty) 操作,empty 永遠 都為 0,導致生產者永遠等待下,不會釋放鎖,消費者因此也會永遠等待下去。
4. 管程
使用信號量機制實現的生產者消費者問題需要客戶端代碼做很多控制,而管程把控制的代碼獨立出來, 不僅不容易出錯,也使得客戶端代碼調用更容易。
c 語言不支持管程,下面的示例代碼使用了類 Pascal 語言來描述管程。示例代碼的管程提供了 insert() 和 remove() 方法,客戶端代碼通過調用這兩個方法來解決生產者-消費者問題。
管程有一個重要特性:在一個時刻只能有一個進程使用管程。進程在無法繼續執行的時候不能一直占用 管程,否則其它進程永遠不能使用管程。
管程引入了 條件變量 以及相關的操作:wait() 和 signal() 來實現同步操作。對條件變量執行 wait() 操 作會導致調用進程阻塞,把管程讓出來給另一個進程持有。signal() 操作用於喚醒被阻塞的進程。