行程同步與臨界區間問題

二元號誌與計數號誌(binary vs counting semaphores)

號誌在日常裡有兩種形態,唯一的差別是計數器的範圍。計數號誌允許它的計數升到任意高;它追蹤的是「好幾個可互換的資源還剩幾個可用」。二元號誌把計數限制在只有 0 或 1;它追蹤的是單一的是/否狀態——空閒或被佔、已發信號或未發。想像停車場的告示牌:計數號誌是那塊「空位:37」、計算許多車位的顯示,而二元號誌則是某一間廁所門上單一的「使用中/空閒」標示。

初始化為 N 的計數號誌一次最多放 N 條執行緒進入,正是「一池 N 個相同資源」的對應工具:N 個緩衝槽、N 條網路連線、N 張工人通行證。每個 wait 取走一張通行證(沒剩時阻塞),每個 signal 歸還一張。初始化為 1 的二元號誌則一次恰好放一條執行緒,那正是互斥——所以二元號誌可當作一把鎖用。事實上,計數號誌可用「若干二元號誌加上一個整數」來模擬,反之亦然,所以兩者在能力上等價;它們的區別在於「你在數什麼」,而非「什麼做得到」。

對「拿二元號誌當鎖」這個想法,有個誠實的提醒:二元號誌與互斥鎖相似卻不相同,而這差別很重要。互斥鎖通常有「擁有者」的概念——只有把它鎖上的那條執行緒才應該解鎖它——而經典的二元號誌沒有擁有者:任何執行緒都可以對它 signal,包括一條從未 wait 過的執行緒。這份彈性對「在不同執行緒間傳遞事件」很有用,但它拿掉了「解開一把你並未持有的鎖」的安全檢查,也意味著二元號誌無法提供倚賴擁有權的功能,例如用來對抗優先權反轉的優先權繼承。所以:要配給 N 個資源就用計數號誌,要做以擁有權為基礎的互斥就用互斥鎖,而二元號誌主要用在你真正需要跨執行緒傳遞信號、而非需要一把鎖的時候。

一個有 10 個槽的有限緩衝區,使用計數號誌 empty = 10(生產者對它 wait)與 full = 0(消費者對它 wait)。另用一個二元號誌 mutex = 1 守護緩衝區內部的指標。計數的那個負責配給槽位;二元的那個負責強制一次一個的存取。

計數(0..N)配給多個資源;二元(0/1)是單一的是/否——可當作鎖用。

二元號誌並不完全等於互斥鎖:經典號誌沒有擁有者(任何執行緒都能 signal)也沒有優先權繼承,所以以擁有權為基礎的上鎖請優先用真正的互斥鎖。

又稱
binary semaphorecounting semaphore二元號誌計數號誌