多執行緒資料同步

當多個執行緒可以呼叫單一物件的屬性與方法時,這些呼叫必須同步。 否則一個執行緒可能會中斷另一個執行緒正在做的事,導致物件處於無效狀態。 其成員不受此類中斷影響的類別,稱為執行緒安全。

.NET 提供多種策略來同步存取實例與靜態成員:

  • 同步的程式碼區域。 你可以利用 Monitor 這個類別或編譯器支援,只同步需要同步的程式碼區塊,提升效能。

  • 手動同步。 你可以使用 .NET 類別函式庫提供的同步物件。 請參閱 同步原語概述,其中包含該 Monitor 類別的討論。

  • 同步的情境。 僅適用於 .NET Framework 應用程式,您可以使用 SynchronizationAttributeContextBoundObject 物件啟用簡單的自動同步。

  • 命名空間中的 System.Collections.Concurrent 集合類別。 這些類別提供內建的同步加減操作。 如需詳細資訊,請參閱 Thread-Safe 集合

通用語言執行時提供一種執行緒模型,類別可依需求以多種方式同步。 下表顯示對於特定同步類別的欄位與方法所提供的同步支援。

Category 全域欄位 靜態欄位 靜態方法 實例欄位 實例方法 特定代碼區塊
沒有同步 No No No No No No
同步情境 No No No Yes Yes No
同步碼區域 No No 僅在已標示時 No 僅在已標記時 僅在已標記時
手動同步 Manual Manual Manual Manual Manual Manual

無同步處理

這是物件的預設設定。 任何執行緒都可以在任何時間存取任何方法或欄位。 一次只能有一個執行緒存取這些物件。

手動同步

.NET 類別函式庫提供多種用於同步執行緒的類別。 請參見 同步原語概述

同步碼區域

你可以使用 Monitor 類別或編譯器關鍵字來同步程式碼區塊、實例方法和靜態方法。 不支援同步靜態場。

Visual Basic 和 C# 都支援使用特定的語言關鍵字來標記程式碼區塊:在 C# 中是 lock 陳述式,在 Visual Basic 中是 SyncLock 陳述式。 當執行緒執行程式碼時,會嘗試取得該鎖。 如果鎖已被其他執行緒取得,執行緒會阻塞直到鎖可用。 當執行緒退出同步的程式碼區塊時,無論執行緒如何離開區塊,鎖都會被釋放。

Note

在 .NET 9 和 C# 13 或更新版本中,如果lock目標是專用System.Threading.Lock實例,lock該敘述會用Lock.EnterScope()來建立同步區域。

Visual Basic 在 SyncLock 中不支援 System.Threading.Lock,因此請繼續使用 SyncLock,並搭配專用的私有參考型別。

如果 lock 目標不是 Lock 實例,且對所有 SyncLock 使用,編譯器會使用 Monitor.EnterMonitor.Exit。 因為這個實作,你仍然可以在同步區域中使用其他 Monitor 方法。

你也可以為方法加上值為 MethodImplOptions.SynchronizedMethodImplAttribute,其效果與使用 Monitor 或其中一個編譯器關鍵字來鎖定整個方法主體相同。

Thread.Interrupt 可用於將執行緒從阻塞操作中解救出來,例如等待存取同步的程式碼區域。 Thread.Interrupt 也用於將執行緒從操作中拆解出來,例如 Thread.Sleep

Important

不要鎖定型別——也就是typeof(MyType)在 C#、 GetType(MyType) Visual Basic 或 MyType::typeid C++ 中——以保護static方法(SharedVisual Basic 中的方法)。 改用私有靜態物件吧。 同樣地,不要在 C#(thisVisual Basic)中用Me來鎖定實例方法。 改用私有物件。 類別或實例可能被非你自己的程式碼鎖定,可能導致死結或效能問題。

編譯器支援

Visual Basic 和 C# 都支援一個語言關鍵字,該關鍵字會使用 Monitor.EnterMonitor.Exit 來鎖定物件。 Visual Basic 支援 SyncLock 語句;C# 支援 lock 陳述句。

在這兩種情況下,如果程式碼區塊中拋出例外,由 lockSyncLock 取得的鎖定都會自動釋放。 C# 和 Visual Basic 編譯器會產生一個 try/finally 區塊,在 try 的開頭呼叫 Monitor.Enter,並在 finally 區塊中呼叫 Monitor.Exit。 如果在 lockSyncLock 區塊內擲回例外,finally 處理常式就會執行,以便讓你進行任何清理工作。

同步脈絡

僅限於 .NET Framework 應用程式,你可以在任何 ContextBoundObject 上使用 SynchronizationAttribute,以同步所有執行個體方法和欄位。 同一上下文域中的所有物件共用相同的鎖。 允許多個執行緒存取方法與欄位,但同一時間僅允許單一執行緒存取。

另請參閱