Semaphore 和 SemaphoreSlim

類別 System.Threading.Semaphore 代表具名 (systemwide) 或本機號誌。 它是一個薄薄的包裝,包裹在 Win32 的 semaphore 物件上。 Win32 的信號量是控制資源池存取權的信號量。

SemaphoreSlim 類別代表一種輕量且快速的信號量,可用於單一程序內的等待,當等待時間預期會較短時。 在旋轉等待階段,CPU 會主動旋轉——它並非閒置。 等待時間需要多短取決於等待的性質:如果執行緒在爭奪 CPU 資源,旋轉執行緒會消耗 CPU 時間,因此等待時間應該非常短,以微秒為單位。 若等待的資源是非 CPU 受限資源(例如 I/O),忙等待的開銷較不重要,因此可接受的等待時間可以稍微延長,以毫秒計算。 當等待時間不可預測或預期會很長時,可以用不會旋轉的 System.Threading.Semaphore 來代替。 SemaphoreSlim 盡可能依賴 Common Language Runtime (CLR) 所提供的同步處理基本類型。 不過,它也會視需要提供延遲初始化的核心型等候句柄,以支援等候多個旗號。 SemaphoreSlim 也支援使用取消標記,但不支援命名信號量或使用等待控制項來同步。

管理有限資源

執行緒會根據信號量的類型,呼叫不同的方法以進入信號量。 對於物件 System.Threading.Semaphore ,呼叫方法 WaitOne (繼承自 WaitHandle)。 對於 SemaphoreSlim 物件,呼叫SemaphoreSlim.Wait 方法或SemaphoreSlim.WaitAsync 方法。 當呼叫傳回時,信號上的計數會遞減。 當執行緒請求進入且計數為零時,執行緒會阻塞。 當執行緒透過呼叫 Semaphore.Release or SemaphoreSlim.Release 方法釋放信號量時,阻塞的執行緒可以進入。 沒有保證順序——例如先入先出(FIFO)或後進先出(LIFO)——會決定哪個阻塞的線程接著進入信號量。

線程可以重複呼叫 System.Threading.Semaphore 物件的 WaitOne 方法或 SemaphoreSlim 物件的 Wait 方法,多次進入號誌。 要釋放信號量,應呼叫 Semaphore.Release()SemaphoreSlim.Release() 方法和執行緒進入的次數相同。 或者,呼叫Semaphore.Release(Int32)SemaphoreSlim.Release(Int32)重載,並指定要釋放的條目數量。

信號量與線程識別

這兩種信號量類型在呼叫 WaitOneWaitReleaseSemaphoreSlim.Release 方法時不會強制執行緒身份。 例如,信號的常見使用案例牽涉到生產者線程和取用者線程,其中一個線程一律遞增旗號計數,另一個線程一律遞減。

確保執行緒不會釋放信號量太多次。 例如,假設信號的最大計數為 2,而執行緒 A 和執行緒 B 都已經進入信號。 如果線程 B 中的程式設計錯誤導致呼叫 Release 兩次,這兩個呼叫都會成功。 號誌上的計數已滿,當執行緒 A 最終呼叫 Release 時,會拋出 SemaphoreFullException

命名信號量

Windows作業系統允許信號具有名稱。 命名的信號量是系統性的——一旦建立,所有執行緒和所有程序都能看到。 命名的信號量因此能同步程序與執行緒的活動。

使用指定名稱的其中一個建構子,建立一個代表命名系統信號量的Semaphore物件。

這很重要

由於命名旗號是系統性的,因此可以有多個 Semaphore 物件代表同一個命名的旗號。 每次呼叫建構函式或 Semaphore.OpenExisting 方法時,都會建立新的 Semaphore 物件。 指定相同名稱會重複建立多個代表相同具名旗號的物件。

當您使用具名旗號時,請小心。 因為這些程序是系統性的,另一個使用相同名稱的程序可能會意外進入你的信號量。 在同一部計算機上執行的惡意代碼可以使用此程式代碼作為阻斷服務攻擊的基礎。

使用訪問控制安全性來保護 Semaphore 代表具名號誌的物件,最好是使用指定 System.Security.AccessControl.SemaphoreSecurity 物件的建構函式。 你也可以用這種 Semaphore.SetAccessControl 方法套用存取控制安全,但這會留下從信號量建立到被保護之間的漏洞時段。 用存取控制安全保護信號量有助於防止惡意攻擊,但無法解決無意名稱衝突的問題。

另請參閱