2010-01-22 67 views
6

您能描述兩種同步在類成員上執行多線程寫入訪問的方法 嗎?.NET線程訪問問題

請任何人都可以幫助我這是什麼意思,什麼是正確的答案。

回答

13

當您在C#中更改數據時,看起來像單個操作的東西可能會編譯成若干指令。看看下面的類:

public class Number { 
    private int a = 0; 
    public void Add(int b) { 
     a += b; 
    } 
} 

當你蓋了,你會得到下面的IL代碼:

IL_0000: nop 
IL_0001: ldarg.0 
IL_0002: dup 
// Pushes the value of the private variable 'a' onto the stack 
IL_0003: ldfld  int32 Simple.Number::a 
// Pushes the value of the argument 'b' onto the stack 
IL_0008: ldarg.1 
// Adds the top two values of the stack together 
IL_0009: add 
// Sets 'a' to the value on top of the stack 
IL_000a: stfld  int32 Simple.Number::a 
IL_000f: ret 

現在,假設你有一個Number對象和兩個線程調用其Add方法是這樣的:

number.Add(2); // Thread 1 
number.Add(3); // Thread 2 

如果您想要結果爲5(0 + 2 + 3),則會出現問題。你不知道這些線程何時執行他們的指令。這兩個線程可以執行IL_0003(推零壓入堆棧),要麼執行IL_000a之前(實際上改變的是成員變量),你會得到這樣的:

a = 0 + 2; // Thread 1 
a = 0 + 3; // Thread 2 

最後一個線程來完成「勝利」,並在該過程結束,a是2或3而不是5.

因此,您必須確保一套完整的指令在另一套完成之前完成。要做到這一點,您可以:

1)鎖定訪問類的成員,而它的寫入,使用許多.NET synchronization primitives之一(如lockMutexReaderWriterLockSlim等),所以只有一個線程可以在它的工作一次。

2)將寫入操作推入隊列並使用單個線程處理該隊列。正如Thorarin指出的那樣,如果它不是線程安全的,您仍然必須同步隊列的訪問權限,但它對於複雜的寫入操作是值得的。

還有其他技術。有些(如Interlocked)僅限於特定的數據類型,甚至更多(如Non-blocking synchronizationPart 4 of Joseph Albahari's Threading in C#中討論的那些),儘管它們更復雜:小心處理它們。

+0

ReadWriterLockSlim爲+1。然而,我認爲OP在完全理解這個概念之前需要更多地熟悉多線程編程。至於你的第二點:你需要同步隊列,使問題複雜化。 – Thorarin 2010-01-22 14:18:10

+0

真的夠了,你的回答爲這些概念提供了一個非常優秀的介紹(雖然我試圖在更新中採取不同的方式)。關於隊列,確實也必須同步,儘管有些情況下這種方法可以簡化事情而不是使事情複雜化! – 2010-01-22 14:24:35

12

在多線程應用程序中,很多情況下同時訪問相同的數據會導致問題。在這種情況下,需要同步來確保一次只有一個線程可以訪問。

我想他們的意思是使用lock-statement(或VB.NET中的SyncLock)與使用Monitor

您可能想要read this page的例子和理解的概念。但是,如果您對多線程應用程序設計沒有經驗,如果您的新僱主讓您參加測試,它可能會很快變得明顯。這是一個相當複雜的主題,有很多可能的陷阱,如deadlock

還有一個體面的MSDN page on the subject以及。

可能有其他選項,具體取決於成員變量的類型以及如何更改。遞增整數例如可以通過Interlocked.Increment方法完成。

作爲問題的練習和演示,請嘗試編寫一個啓動5個併發線程的應用程序,每個線程增加一個共享計數器一百萬次。該計數器的預期最終結果將是500萬,但是(可能)不是你將以最終結果:)

編輯:自己做了一個快速實現(download)。示例輸出:

Unsynchronized counter demo: 
expected counter = 5000000 
actual counter = 4901600 
Time taken (ms) = 67 

Synchronized counter demo: 
expected counter = 5000000 
actual counter = 5000000 
Time taken (ms) = 287 
+0

感謝您的回答。我想知道更多關於.NET中的多線程(C#)。事實上,儘管我一般需要更多地瞭解c#。你會建議我一本書,它可以將一個普通的開發人員轉變爲下一代開發人員。 – Supremestar 2010-01-22 15:03:50

+0

沒有特定的標題,但我不太多用書。事實上,我已經預先訂購了第二版的C#,這真是太神奇了。好東西,但可能有些書更適合您的需求。 – Thorarin 2010-01-22 15:11:00

+2

「C#深度」是一本很棒的書。 – 2010-01-22 15:18:48

0

有幾種方法,其中有幾種方法是前面提到的。

  1. ReaderWriterLockSlim是我的首選方法。這給你一個數據庫類型的鎖定,並允許升級(儘管我上次看到的MSDN語法是不正確的,而且非常不明顯)
  2. 鎖定語句。您將讀取視爲寫入操作,只是防止訪問變量
  3. 聯鎖操作。這在原子步驟中對值類型執行操作。這可以用於無鎖的線程(真的不推薦這)
  4. 互斥和信號燈(沒有使用這些)
  5. 監控報表(這基本上是鎖定的關鍵字是如何工作的)

雖然我不想詆譭其他答案,但我不相信任何不使用這些技術的東西。如果我忘記了任何,我很抱歉。