同步處理原始物件概觀
.NET 提供可用來同步對共用資源的存取或協調執行緒互動的一系列類型。
重要
使用相同同步處理原始物件執行個體來保護共用資源的存取。 如果您使用不同的同步處理原始物件執行個體來保護相同資源,則會規避同步處理原始物件所提供的保護。
WaitHandle 類別和輕量型同步處理類別
衍生自 System.Threading.WaitHandle 類別的多個 .NET 同步處理原始物件,它能封裝原生作業系統同步處理控制代碼,並針對執行緒互動使用訊號機制。 那些類別包括:
- System.Threading.Mutex,它能授與對共用資源的獨佔存取權。 如果沒有任何執行緒擁有 Mutex,則 Mutex 的狀態為已發出訊號。
- System.Threading.Semaphore,它能限制可以同時存取共用資源或資源集區的執行緒數目。 旗號的狀態會在其計數大於零時設定為已收到訊號,並在計數為零時設定為未收到訊號。
- System.Threading.EventWaitHandle,它代表執行緒同步處理事件,並可以處於已收到訊號或未收到訊號的狀態。
- System.Threading.AutoResetEvent,它衍生自 EventWaitHandle,且在收到訊號時,會在發佈單一等候執行緒之後自動重設至未收到訊號的狀態。
- System.Threading.ManualResetEvent,它衍生自 EventWaitHandle,且在收到訊號時會持續處於已收到訊號的狀態,直到 Reset 方法被呼叫為止。
在 .NET Framework 中,由於 WaitHandle 衍生自 System.MarshalByRefObject,因此這些類型可用來跨應用程式定義域界限同步處理執行緒的活動。
在 .NET Framework、.NET Core 和 .NET 5+ 中,這些類型有一部分可以代表具名系統同步處理控制代碼;其於整個作業系統中皆可見,並可以用來進行處理序間的同步處理:
- Mutex
- Semaphore (在 Windows 上)
- EventWaitHandle (在 Windows 上)
如需詳細資訊,請參閱 WaitHandle API 參考。
輕量型同步處理類型不會仰賴基礎作業系統控制代碼,且通常能提供較佳的效能。 不過,它們並無法用於處理序間的同步處理。 請針對位於單一應用程式內的執行緒同步處理使用那些類型。
那些類型有一部分是衍生自 WaitHandle 之類型的替代方案。 例如,SemaphoreSlim 是 Semaphore 的輕量型替代方案。
對共用資源之存取的同步處理
.NET 提供一系列的同步處理原始物件,以控制多個執行緒對共用資源的存取。
Monitor 類別
System.Threading.Monitor 類別會透過取得或釋放能識別共用資源之物件上的鎖定,來授與對該資源的互斥存取。 持有鎖定時,持有鎖定的執行緒可以再次取得並釋放鎖定。 其他執行緒將無法取得鎖定,且 Monitor.Enter 方法會等待直到釋放鎖定為止。 Enter 方法會取得已釋放的鎖定。 您也可以使用 Monitor.TryEnter 方法來指定執行緒嘗試取得鎖定的時間長度。 由於 Monitor 類別具有執行緒同質性,因此取得鎖定的執行緒必須呼叫 Monitor.Exit 方法來釋放鎖定。
您可以使用 Monitor.Wait、Monitor.Pulse 與 Monitor.PulseAll 方法來協調在相同物件上取得鎖定之執行緒的互動。
如需詳細資訊,請參閱 Monitor API 參考。
注意
請使用以 C# 撰寫的 lock 陳述式,以及使用 Visual Basic 撰寫的 SyncLock 陳述式來同步對共用資源的存取,而不要直接使用 Monitor。 那些陳述式是使用 Enter 與 Exit 方法來實作,並會使用 try…finally
區塊以確保所取得的鎖定一律會被釋放。
Mutex 類別
System.Threading.Mutex 類別,它和 Monitor 相同,能授與對共用資源的獨佔存取權。 使用其中一個 Mutex.WaitOne 方法多載來要求 Mutex 的擁有權。 與 Monitor 類似,Mutex 具有執行緒同質性,且取得 Mutex 的執行緒必須呼叫 Mutex.ReleaseMutex 方法來釋放它。
與 Monitor 不同,Mutex 類別可以用於處理序間的同步處理。 若要這麼做,請使用具名 Mutex,這能使其於整個作業系統中皆可見。 若要建立具名 Mutex 執行個體,請使用能指定名稱的 Mutex 建構函式。 您也可以呼叫 Mutex.OpenExisting 方法來開啟現有的具名系統 Mutex。
如需詳細資訊,請參閱 Mutex 文章與 Mutex API 參考文件。
SpinLock 結構
System.Threading.SpinLock 結構與 Monitor 類似,能根據鎖定的可用性授與對共用資源的獨佔存取。 當 SpinLock 嘗試取得不可用的鎖定時,它會在迴圈中等候並重複檢查,直到該鎖定變得可用為止。
如需使用微調鎖定之優缺點的詳細資訊,請參閱 SpinLock 文章與 SpinLock API 參考。
ReaderWriterLockSlim 類別
System.Threading.ReaderWriterLockSlim 類別會授與對共用資源進行寫入的獨佔存取權,並允許多個執行緒同時存取該資源以進行讀取。 您應該使用 ReaderWriterLockSlim 來同步對支援安全執行緒讀取作業,但需要獨佔存取以執行寫入作業之共用資料結構的存取。 當執行緒要求獨佔存取權 (例如,透過呼叫 ReaderWriterLockSlim.EnterWriteLock 方法) 時,後續的讀取器和寫入器要求會封鎖直到所有現有讀取器都結束鎖定,且寫入器已進入並離開鎖定為止。
如需詳細資訊,請參閱 ReaderWriterLockSlim API 參考。
旗號與 SemaphoreSlim 類別
System.Threading.Semaphore 與 System.Threading.SemaphoreSlim 類別能限制可以同時存取共用資源或資源集區的執行緒數目。 要求資源的其他執行緒需等候,直到執行緒釋放旗號為止。 由於旗號不具有執行緒同質性,因此某個執行緒可以取得旗號,並由另一個執行緒釋放它。
SemaphoreSlim 是 Semaphore 的輕量型替代方案,並僅可用於在單一處理序界限內進行同步處理。
在 Windows 上,您可以使用 Semaphore 以進行處理序間的同步處理。 若要這麼做,請使用其中一個能指定名稱的旗號建構函式或 Semaphore.OpenExisting 方法,建立代表具名系統旗號的 Semaphore 執行個體。 SemaphoreSlim 不支援具名系統旗號。
如需詳細資訊,請參閱旗號與 SemaphoreSlim 文章,以及 Semaphore 或 SemaphoreSlim API 參考。
執行緒互動 (或訊號處理)
執行緒互動 (或執行緒訊號處理) 表示執行緒必須等候來自一或多個執行緒的通知 (或訊號),才能繼續執行。 例如,如果執行緒 A 呼叫執行緒 B 的 Thread.Join 方法,在執行緒 B 完成之前,執行緒 A 將會被封鎖。 上一節所描述的同步處理原始物件能提供不同的訊號處理機制:執行緒可以透過釋放鎖定,來通知另一個執行緒其可以取得鎖定來繼續執行。
此節描述由 .NET 提供的其他訊號處理建構。
EventWaitHandle、AutoResetEvent、ManualResetEvent 與 ManualResetEventSlim 類別
System.Threading.EventWaitHandle 類別代表執行緒同步處理事件。
同步處理事件可以處於未收到訊號或已收到訊號的狀態。 當事件的狀態為未收到訊號時,呼叫該事件之 WaitOne 多載的執行緒會被封鎖,直到事件收到訊號為止。 EventWaitHandle.Set 方法會將事件的狀態設定為已收到訊號。
已收到訊號之 EventWaitHandle 的行為會取決於其重設模式:
- 搭配 EventResetMode.AutoReset 旗標建立的 EventWaitHandle,會在發佈單一等候執行緒後自動重設。 它就像是在每次收到訊號時僅允許單一執行緒通過的十字轉門一般。 System.Threading.AutoResetEvent 類別 (衍生自 EventWaitHandle) 代表該行為。
- 搭配 EventResetMode.ManualReset 旗標建立的 EventWaitHandle 會維持已收到訊號的狀態,直到其 Reset 方法被呼叫為止。 它就像是在收到訊號前會保持關閉,並在有人重新關閉它前會持續開啟的閘門一般。 System.Threading.ManualResetEvent 類別 (衍生自 EventWaitHandle) 代表該行為。 System.Threading.ManualResetEventSlim 類別是 ManualResetEvent 的輕量型替代方案。
在 Windows 上,您可以使用 EventWaitHandle 以進行處理序間的同步處理。 若要這麼做,請使用其中一個能指定名稱的 EventWaitHandle 建構函式或 EventWaitHandle.OpenExisting 方法,建立代表具名系統同步處理事件的 EventWaitHandle 執行個體。
如需詳細資訊,請參閱 EventWaitHandle 文章。 如需 API 參考,請參閱 EventWaitHandle、AutoResetEvent、ManualResetEvent 與 ManualResetEventSlim。
CountdownEvent 類別
System.Threading.CountdownEvent 類別代表會在計數為零時被設定的事件。 在 CountdownEvent.CurrentCount 大於零時,呼叫 CountdownEvent.Wait 的執行緒將會被封鎖。 呼叫 CountdownEvent.Signal 來使事件的計數遞減。
與可透過來自單一執行緒的訊號將多個執行緒解除封鎖的 ManualResetEvent 或 ManualResetEventSlim 相反,您可以使用 CountdownEvent 以透過來自多個執行緒的訊號,將一或多個執行緒解除封鎖。
如需詳細資訊,請參閱 CountdownEvent 文章與 CountdownEvent API 參考。
Barrier 類別
System.Threading.Barrier 類別代表執行緒執行屏障。 呼叫 Barrier.SignalAndWait 方法的執行緒會發出其已抵達屏障的訊號,並會持續等候,直到其他參與者執行緒也抵達屏障為止。 當所有參與者執行緒皆抵達屏障時,它們便會繼續執行,而該屏障也會重設並可供再次使用。
當有一或多個執行緒需要取得其他執行緒的結果以繼續至下個運算階段時,您可以使用 Barrier。
如需詳細資訊,請參閱屏障文章與 Barrier API 參考。
Interlocked 類別
System.Threading.Interlocked 類別可提供能針對變數執行簡易不可部分完成之作業的靜態方法。 那些不可部分完成的作業包括對 64 位元整數值的相加、遞增和遞減、根據比較的交換和條件式交換,以及讀取作業。
如需詳細資訊,請參閱 Interlocked API 參考。
SpinWait 結構
System.Threading.SpinWait 結構能提供微調式等候的支援。 當執行緒必須等待事件收到訊號或符合條件,但實際的等待時間預期會少於使用等候控制代碼或以其他方式封鎖執行緒的等候時間時,您便可以使用它。 使用 SpinWait,您可以指定在等待時旋轉一小段時間,並且只有在指定的時間內未符合條件時放棄 (例如,藉由等待或睡眠)。
如需詳細資訊,請參閱 SpinWait 文章與 SpinWait API 參考。