[C#.NET][Thread] 執行緒資源存取限制的同步機制 - Semaphore
Semaphore類別是用來限制執行緒同時存取資源的機制,它是用號誌計數來限制執行緒的鎖定,每當執行緒進入號誌(WaitOne)時,號誌上的計數會遞減,而在執行緒釋放(Release)號誌時,計數會遞增;我們也是可以利用它來等候其他的執行緒進而達到資料同步機制
同時它也是繼承WaitHandle來的~
另外有一點很重要
Semaphore 類別在對 WaitOne 或 Release 的呼叫上不會強制使用執行緒識別,確保執行緒不會將號誌釋放太過多次是程式設計人員的責任,若號誌計數已滿卻又呼叫 Release 方法,會擲回 SemaphoreFullException。
接下來就來用程式碼來觀察其狀態
由範例得知五條執行緒都沒有執行37行,因為都被Semaphore.WaitOne方法鎖定了,執行緒們的狀態進入到了WaitSleepJoin,等待有人來解放它們。
我們這兩行註解打開看看會有什麼結果
Console.WriteLine("Main thread calls Release(3).");
_pool.Release(3);
我釋放了三個鎖定,我們可以看到只有三條執行緒能順利的執行到37行,Release後的執行緒狀態也回到Running了;封鎖的執行緒進入號誌的順序並沒有一定 (例如 FIFO 或 LIFO),總之它還是靠系統自己決定要放誰進來。
倘若我放太多執行緒進來將會出錯
MSDN有這麼一段:
號誌有兩種類型:區域號誌和具名系統號誌。 如果您使用接受名稱的建構函式建立 Semaphore 物件,則該物件會與該名稱的作業系統號誌產生關聯。 在整個作業系統中都可以看到具名系統號誌,而且這種號誌也可以用於同步化處理序的活動。 您可以建立多個 Semaphore 物件來表示相同的具名系統號誌,也可以使用 OpenExisting 方法開啟現有的具名系統號誌。
區域號誌只能存在於處理序中。 處理序中具有區域 Semaphore 物件參考的任何執行緒都可以使用這種號誌。 每一個 Semaphore 物件都是一個個別的區域號誌。
這表示在作業系統下的不同應用程式可以用同一個具名號誌,馬上就來演練,我會先寫一隻具名的Semaphore 應用程式,
_pool = new Semaphore(0, 5, "mySemaphore");//建立系統具名號誌
再寫另一隻應用程式開啟具名號誌
_pool = Semaphore.OpenExisting("mySemaphore");//開啟具名號誌並引用至區域號誌
這是ConsoleApplication1,使用具名方式建立
若ConsoleApplication1已開啟,我再執行ConsoleApplication2,便可開啟已存在的具名號誌;反之,若沒先執行ConsoleApplication1,就先執行ConsoleApplication2是會出錯的。
由於
每一個 Semaphore 物件都是一個個別的區域號誌。
所以ConsoleApplication2開啟的具名號誌也是另外一個執行個體,可以說是複製ConsoleApplication1來的,實行個體是獨立分開的,至於這個複製動作是深層還是淺層我就沒研究了。
倘若我在ConsoleApplication1定義計數器上限是3,在ConsoleApplication2裡的Release就不能大於3,否則會拋出例外。
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET