2011-06-14 120 views
21

如果您有一個帶有委託成員變量的類實例,並且多個線程調用該委託(假設它指向長時間運行的方法),那麼是否存在爭用問題?C#委託是否線程安全?

您是否需要鎖定委託或每個線程都可以安全地調用委託指向的方法,因爲每個線程都有自己的調用堆棧?

回答

7

不,他們不是線程安全的,是的,你必須自己管理併發。

+0

關於代表可能會發生什麼變化? (你的評論對於事件是正確的,但我沒有看到關於委託人可以改變什麼)。 – 2011-06-14 19:49:08

+1

@ agent-j:那不重要;底層的實現可以改變,但是當前的文檔如前所述。 「 – 2011-06-14 20:06:36

6

直接從MulticastDelegate文檔:

任何公共靜態此類型的成員(在Visual Basic中的Shared)都是線程安全的。任何實例成員不保證是線程安全的。

Delegate類包含相同的信息,所以你有它。

2

修改事件不是線程安全的,但調用委託是。 由於委託是不可變的,所以它是線程安全的。見備註這裏MSDN Delegate class

here借: 在CLR通過C#里氏指出了有關在多線程類事件調用一些細微的問題:

委託鏈是不可變的;一個新的鏈條被創建以取代第一個鏈條。 零訂戶的委託鏈爲空。 這意味着(如果您的事件是公開的),它可能隨時從空變爲非空,反之亦然。

+2

」任何實例成員不保證是線程安全的。「 - 從您的鏈接 – heisenberg 2011-06-14 19:47:37

23

關於調用委託,答案是肯定的。

調用委託是線程安全的,因爲委託是不可變的。但是,您必須確保首先存在委託。此檢查可能需要一些同步機制,具體取決於所需的安全級別。

例如,如果SomeDelegate被空檢查和調用之間的另一個線程設置爲null,則以下情況可能會拋出NullReferenceException

if (SomeDelegate != null) 
{  
    SomeDelegate(); 
} 

以下是更安全一點。在這裏,我們正在利用代表是不可改變的事實。即使另一個線程修改了SomeDelegate代碼也很難防止那個討厭的NullReferenceException

Action local = SomeDelegate; 
if (local != null) 
{ 
    local(); 
} 

然而,這可能會導致如果SomeDelegate被分配在另一個線程非空值永遠不會被執行的委託。這與微妙的記憶障礙問題有關。以下是最安全的方法。

Action local = Interlocked.CompareExchange(ref SomeDelegate, null, null); 
if (local != null) 
{ 
    local(); 
} 

關於代表引用的方法的執行答案是否定的。

您必須通過使用同步機制來提供您自己的線程安全保證。這是因爲CLR不會自動爲執行代表提供線程安全保證。可能是這種方法不需要任何進一步的同步來使其安全,特別是如果它從不訪問共享狀態。但是,如果方法讀取或寫入共享變量,那麼您將不得不考慮如何防止來自多個線程的併發訪問。

+0

或者您可以簡單地將其聲明爲「公共事件SomeHandler MyEvent = {}」,並且它保證不爲空。 – 2011-06-14 20:13:01

+1

@Ed:適用於事件,但它不適用於原始代理,因爲您可以執行'SomeDelegate = null'。 – 2011-06-14 20:15:17

+0

是的這是真的,我是(錯誤地)承擔事件。 – 2011-06-14 20:15:44