共用方式為


同步處理原始物件概觀

.NET Framework 提供了各種同步處理原始物件,可用來控制執行緒的互動以及避免競爭情形。 這些可大略分為三種:鎖定、信號及連鎖作業。

這些分類並未清楚地定義:某些同步處理機制具有多個分類的特性、一次釋放單一執行緒的事件之作用就像是鎖定、任何鎖定的釋放都可視為信號、連鎖作業可用來建構鎖定。 但是,這些分類仍然相當實用。

請牢記執行緒同步處理是合作性質的,這一點很重要。 如果有一個執行緒略過同步處理機制,並直接存取受保護的資源,則該同步處理機制就不會是有效的。

本概觀包含下列各節:

  • 鎖定

  • 信號

  • 輕量型同步處理型別

  • SpinWait

  • 連鎖作業

鎖定

鎖定一次會為一個執行緒或指定數目的執行緒提供資源的控制權; 當鎖定在使用中時,要求獨佔鎖定的執行緒會封鎖,一直到可使用該鎖定為止。

獨佔鎖定

鎖定的最簡單之形式就是 C# lock 陳述式 (Visual Basic 中為 SyncLock),它可控制對程式碼區塊的存取權; 這類區塊通常稱為關鍵區段。 lock 陳述式的實作方式,是利用 Monitor 類別的 EnterExit 方法,且它會使用 try…catch…finally 來確定鎖定已釋放。

一般來說,使用 lock 陳述式來保護小型程式碼區塊,而絕對不要擴展超過一個以上的方法,是使用 Monitor 類別的最佳方法。 Monitor 類別雖然功能強大,但卻容易使鎖定和死結變為孤立。

Monitor 類別

Monitor 類別可提供可以與 lock 陳述式一起搭配使用的額外功能。

  • TryEnter 方法可讓被封鎖的執行緒等候資源在指定之間隔時間之後放棄, 它會傳回指出成功或失敗的布林值 (Boolean),這個值可用來偵測及避免可能的死結。

  • Wait 方法是由關鍵區段中的執行緒所呼叫, 它會放棄資源的控制權並封鎖,一直到資源再次可用為止。

  • PulsePulseAll 方法可讓即將要釋放鎖定或呼叫 Wait 的一個執行緒將一個或多個執行緒置入備妥的佇列中,讓它們可以取得鎖定。

Wait 方法多載上的逾時可讓等候中的執行緒離開備妥的佇列。

Monitor 類別可以在多個應用程式定義域中提供鎖定,但前提是用於該鎖定的物件要衍生自 MarshalByRefObject

Monitor 具有執行緒相似性; 也就是說,進入 Monitor 的執行緒必須藉由呼叫 ExitWait 來離開。

Monitor 類別無法執行個體化, 它的方法為靜態 (Visual Basic 中為 Shared),且會在可執行個體化的鎖定物件上運作。

如需概觀說明,請參閱監視器

Mutex 類別

執行緒要求 Mutex 的方式,是藉由呼叫它的 WaitOne 方法之多載。 會提供具有逾時的多載,好讓執行緒可以放棄等候。 Mutex 不像 Monitor 類別,它可以是區域或全域。 全域 Mutex (也稱為具名 Mutex) 在整個作業系統中都可以看到,且可用來在多個應用程式定義域或處理序中同步處理執行緒。 區域 Mutex 衍生自 MarshalByRefObject,且可用在跨應用程式定義域的界限上。

此外,Mutex 衍生自 WaitHandle,這表示它可以與 WaitHandle 提供的信號機制一起搭配使用,例如 WaitAllWaitAnySignalAndWait 方法。

就像 Monitor 一樣,Mutex 也有執行緒相似性。 Mutex 是可執行個體化的物件,與 Monitor 不同。

如需概觀說明,請參閱 Mutex

SpinLock 類別

從 .NET Framework 4 版開始,當 Monitor 所需的額外負荷降低效能時,您就可以使用 SpinLock 類別。 當 SpinLock 遇到鎖定的關鍵區段時,它只會在迴圈中空轉,直到鎖定變成可用為止。 如果鎖定的保留時間非常短暫,空轉可能會比封鎖提供較佳的效能。 不過,如果鎖定的保留時間超過數十個循環,SpinLock 的效能就與 Monitor 一樣,不過前者將使用更多 CPU 循環,因此可能會降低其他執行緒或處理序的效能。

其他鎖定

鎖定不需要是獨佔的; 讓有限數目的執行緒同時存取一個資源時,鎖定會相當實用。 號誌 (Semaphore) 和 Reader-Writer 鎖定的設計目的,是要控制這種類型的集區資源存取。

ReaderWriterLock 類別

ReaderWriterLockSlim 類別所針對的情況,是變更資料的執行緒 (亦即寫入器) 必須有資源的獨佔存取權。 當寫入器不在使用中時,任何數目的讀取器都可以存取此資源 (例如,藉由呼叫 EnterReadLock 方法)。 當執行緒要求獨佔存取權時 (例如,藉由呼叫 EnterWriteLock 方法),之後的讀取器要求就會封鎖,直到所有現有的讀取器都結束鎖定,以及該寫入器已進入並結束鎖定為止。

ReaderWriterLockSlim 具有執行緒相似性。

如需概觀說明,請參閱 Reader-Writer 鎖定

Semaphore 類別

Semaphore 類別可允許指定數目的執行緒存取某個資源; 其他要求此資源的執行緒會封鎖,一直到執行緒釋放號誌為止。

Semaphore 衍生自 WaitHandle,和 Mutex 類別一樣。 Semaphore 可以是區域或全域,這一點也和 Mutex 一樣; 它可用在跨應用程式定義域的界限內。

Semaphore 沒有執行緒相似性,與 MonitorMutexReaderWriterLock 不同。 這表示,它可用於以下情況:一個執行緒取得號誌,然後另一個執行緒釋放此號誌。

如需概觀說明,請參閱 Semaphore 和 SemaphoreSlim

System.Threading.SemaphoreSlim 是輕量型號誌,適用於在單一處理序界限中同步處理。

回到頁首

信號

等候來自其他執行緒之信號最簡單的方式就是呼叫 Join 方法,以便封鎖直到其他執行緒完成為止。 Join 具有兩個多載,可讓封鎖的執行緒在經過指定的間隔之後中斷等候。

等候控制代碼可提供更多、更豐富的等候和信號功能。

等候控制代碼

等候控制代碼衍生自 WaitHandle 類別,而此類別則衍生自 MarshalByRefObject。 因此,等候控制代碼可用在跨應用程式定義域的界限內,同步處理執行緒的活動。

執行緒在等候控制代碼上封鎖的方式,是藉由呼叫執行個體方法 WaitOneWaitAllWaitAnySignalAndWait 的一個靜態方法; 而釋放的方式,需視所呼叫的方法以及等候控制代碼的類型而定。

如需概觀說明,請參閱 等候控制代碼

事件等候控制代碼

事件等候控制代碼包括 EventWaitHandle 類別和它的衍生類別 AutoResetEventManualResetEvent。 當事件等候控制代碼藉由呼叫它的 Set 方法或使用 SignalAndWait 方法而收到信號時,即會從此事件等候控制代碼中釋放執行緒。

事件等候控制代碼不是會自動將自己重設 (就像轉門,在每次收到信號時,只能讓一個執行緒通過),就是必須以手動方式重設 (就像是閘門,在收到信號之前都是關閉的,然後會開啟到有人將它關閉為止)。 AutoResetEventManualResetEvent 分別表示前者和後者,就像是其名稱所指的一樣。 System.Threading.ManualResetEventSlim 是輕量型事件,適用於在單一處理序界限中同步處理。

EventWaitHandle 可以表示任一事件類型,也可以是區域或全域。 衍生類別 AutoResetEventManualResetEvent 一定都是區域的。

事件等候控制代碼沒有執行緒相似性; 任何執行緒都可以發出事件等候控制代碼信號。

如需概觀說明,請參閱 EventWaitHandle、AutoResetEvent、CountdownEvent 和 ManualResetEvent

Mutex 和 Semaphore 類別

因為 MutexSemaphore 類別是衍生自 WaitHandle,所以可以與 WaitHandle 的靜態方法一起使用。 例如,執行緒可以使用 WaitAll 方法來等候,直到下列三個條件都成立為止:EventWaitHandle 已收到信號、已釋放 Mutex,以及已釋放 Semaphore。 同樣地,執行緒可以使用 WaitAny 方法來等候,直到上面其中任一個條件成立為止。

如果是 MutexSemaphore,正收到信號表示正被釋放。 如果任一型別當做 SignalAndWait 方法的第一個引數使用,則會將它釋放。 如果是具有執行緒相似性的 Mutex,則在呼叫執行緒未主控 Mutex 時,會擲回例外狀況。 如上面所提及,號誌並沒有執行緒相似性。

屏障

Barrier 類別可讓您以循環方式同步處理多個執行緒,讓它們都在相同的時間點封鎖並等候所有其他執行緒完成。 當一個或多個執行緒需要另一個執行緒的結果,才能繼續進行演算法的下一個階段時,屏障就很有用。 如需詳細資訊,請參閱 屏障 (.NET Framework)

回到頁首

輕量型同步處理型別

從 .NET Framework 4 開始,您就可以使用同步處理原始物件,這些物件會盡可能避免對 Win32 核心物件 (例如等候控制代碼) 產生高度耗費資源的仰賴,藉以提供更快的效能。 一般而言,當等候時間很短暫,而且已經嘗試過原始同步處理型別並發現結果令人不滿意時,您就應該使用這些型別。 這些輕量型型別無法用於需要跨處理序通訊的案例中。

回到頁首

SpinWait

從 .NET Framework 4 開始,當某個執行緒必須等候事件變成發出信號或符合條件,但是實際等候時間應該少於使用等候控制代碼或以其他方式封鎖目前執行緒所需的等候時間時,您就可以使用 System.Threading.SpinWait 結構。 您可以使用 SpinWait 來指定一段要在等候時空轉的短時間,然後只有當條件沒有在指定的時間內符合時才放棄 (例如,等候或休眠)。

回到頁首

連鎖作業

連鎖的作業是在記憶體位置上由 Interlocked 類別的靜態方法所執行的簡單且不可部分完成的作業 (Atomic Operation)。 這些不可部分完成的作業包括加法、遞增和遞減、交換、視比較結果而定的條件式交換,32 位元平台上的 64 位元值之讀取作業。

注意事項注意事項

單元性 (Atomicity) 的保證限制為個別的作業;當多個作業必須以一個單位的形式執行時,必須使用比較鬆散的同步處理機制。

雖然這些作業中沒有一個是鎖定或信號,但還是可用來建構鎖定和信號。 因為它們對於 Windows 作業系統而言是原生的,所以連鎖的作業都極為快速。

連鎖的作業可以與動態記憶體保證一起使用,以編寫可呈現強大功能且毫無阻礙之並行性的應用程式。 但是,這些作業需要精密、低階的程式設計,所以就大多數的目的而言,簡單鎖定是較佳的選擇。

如需概觀說明,請參閱 Interlocked 作業

回到頁首

請參閱

概念

同步處理多執行緒處理的資料

監視器

Mutex

Semaphore 和 SemaphoreSlim

等候控制代碼

Interlocked 作業

Reader-Writer 鎖定

屏障 (.NET Framework)

SpinLock

其他資源

EventWaitHandle、AutoResetEvent、CountdownEvent 和 ManualResetEvent

SpinWait