當多個線程可以調用單個對象的屬性和方法時,對這些調用進行同步處理是非常重要的。否則,一個線程可能會中斷另一個線程正在執行的任務,使該對象處於一種無效狀態。其成員不受這類中斷影響的類叫做安全執行緒類。
Common Language Infrastructure 提供了幾種可用來同步對執行個體和靜態成員的訪問的策略:
同步代碼地區。可以使用 Monitor 類或此類的編譯器支援來僅同步需要此類的代碼塊,從而提高效能。
手動同步。可以使用 .NET Framework 類庫提供的同步對象。請參見 同步基元概述,這部分對 Monitor 類進行了討論。
同步上下文。可以使用 SynchronizationAttribute 為 ContextBoundObject 對象啟用簡單的自動同步。
System.Collections.Concurrent 命名空間中的集合類。這些類提供了內建的同步添加和移除操作。有關更多資訊,請參見安全執行緒集合。
通用語言執行平台提供一個執行緒模式,在該模型中,類分為許多類別,這些類別可以根據要求以各種不同的方式進行同步。下表顯示了為具有給定同步類別的欄位和方法提供的同步支援。
類別 |
全域欄位 |
靜態欄位 |
靜態方法 |
執行個體欄位 |
執行個體方法 |
特定代碼塊 |
無同步 |
否 |
否 |
否 |
否 |
否 |
否 |
同步上下文 |
否 |
否 |
否 |
是 |
是 |
否 |
同步代碼地區 |
否 |
否 |
僅當標記時 |
否 |
僅當標記時 |
僅當標記時 |
手動同步 |
手動 |
手動 |
手動 |
手動 |
手動 |
手動 |
無同步
這對於對象是預設情況。任何線程都可以隨時訪問任何方法或欄位。一次只能有一個線程訪問這些對象。
手動同步
.NET Framework 類庫提供大量用於同步線程的類。請參見 同步基元概述。
同步代碼地區
可以使用 Monitor 類或編譯器關鍵字來同步代碼塊、執行個體方法和靜態方法。不支援同步靜態欄位。
Visual Basic 和 C# 都支援使用特定語言關鍵字標記代碼塊,在 C# 中使用的是 lock 語句,在 Visual Basic 中使用的是 SyncLock 語句。當由線程執行該代碼時,會嘗試擷取鎖。如果該鎖已由其他線程擷取,則在鎖變為可用狀態之前,該線程一直處于禁止狀態。當線程退出同步代碼塊時,鎖就會被釋放,它與線程的退出方式無關。
| 說明 |
lock 和 SyncLock 語句是使用 Monitor::Enter 和 Monitor::Exit 實現的,因此,可以在同步地區中將它們與 Monitor 的其他方法一起使用。 |
還可以用 MethodImplAttribute 和 MethodImplOptions.Synchronized 修飾方法,其效果和使用 Monitor 或其中一個編譯器關鍵字鎖定整個方法體相同。
Thread::Interrupt 可用於使線程跳出阻止操作(如等待訪問同步代碼地區)。Thread.Interrupt 還用於使線程跳出 Thread::Sleep 等操作。
| 重要事項 |
為保護 static 方法(Visual Basic 中的 Shared 方法),請不要鎖定類型,即:C# 中的 typeof(MyType)、Visual Basic 中的 GetType(MyType) 或 C++ 中的 MyType::typeid。而應改用私人靜態對象。類似地,不要使用 C# 中的 this(Visual Basic 中的 Me)鎖定執行個體方法。而應使用私人對象。類或執行個體可由其他代碼鎖定,您自己的代碼進行鎖定可能會引起死結或效能問題。 |
編譯器支援
Visual Basic 和 C# 都支援使用 Monitor::Enter 和 Monitor::Exit 來鎖定對象的語言關鍵字。Visual Basic 支援 SyncLock 語句;C# 支援 lock 語句。
這兩種情況下,如果代碼塊中引發異常,則 lock 或 SyncLock 鎖擷取的鎖將自動釋放。C# 和 Visual Basic 編譯器在發出 try/finally 塊時,在 try 的起始處使用 Monitor.Enter,在 finally 塊中使用 Monitor.Exit。如果 lock 或 SyncLock 塊內部引發了異常,則會運行 finally 處理常式,從而使您可以執行任何清除工作。
同步上下文
可以使用任何 ContextBoundObject 的 SynchronizationAttribute 來同步所有執行個體方法和欄位。同一上下文域中的所有對象都共用同一個鎖。允許多個線程存取方法和欄位,但在任一時刻只允許一個線程訪問。