回答
當您在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之一(如lock
,Mutex
,ReaderWriterLockSlim
等),所以只有一個線程可以在它的工作一次。
2)將寫入操作推入隊列並使用單個線程處理該隊列。正如Thorarin指出的那樣,如果它不是線程安全的,您仍然必須同步隊列的訪問權限,但它對於複雜的寫入操作是值得的。
還有其他技術。有些(如Interlocked
)僅限於特定的數據類型,甚至更多(如Non-blocking synchronization和Part 4 of Joseph Albahari's Threading in C#中討論的那些),儘管它們更復雜:小心處理它們。
在多線程應用程序中,很多情況下同時訪問相同的數據會導致問題。在這種情況下,需要同步來確保一次只有一個線程可以訪問。
我想他們的意思是使用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
感謝您的回答。我想知道更多關於.NET中的多線程(C#)。事實上,儘管我一般需要更多地瞭解c#。你會建議我一本書,它可以將一個普通的開發人員轉變爲下一代開發人員。 – Supremestar 2010-01-22 15:03:50
沒有特定的標題,但我不太多用書。事實上,我已經預先訂購了第二版的C#,這真是太神奇了。好東西,但可能有些書更適合您的需求。 – Thorarin 2010-01-22 15:11:00
「C#深度」是一本很棒的書。 – 2010-01-22 15:18:48
有幾種方法,其中有幾種方法是前面提到的。
- ReaderWriterLockSlim是我的首選方法。這給你一個數據庫類型的鎖定,並允許升級(儘管我上次看到的MSDN語法是不正確的,而且非常不明顯)
- 鎖定語句。您將讀取視爲寫入操作,只是防止訪問變量
- 聯鎖操作。這在原子步驟中對值類型執行操作。這可以用於無鎖的線程(真的不推薦這)
- 互斥和信號燈(沒有使用這些)
- 監控報表(這基本上是鎖定的關鍵字是如何工作的)
雖然我不想詆譭其他答案,但我不相信任何不使用這些技術的東西。如果我忘記了任何,我很抱歉。
- 1. .NET線程問題
- 2. c#線程訪問問題
- 3. C#.NET線程問題
- 4. .NET線程 - 快速問題
- 5. .NET線程問題,TcpClient
- 6. Dispatcher.Invoke和線程訪問的問題
- 7. C#線程 - 父訪問問題
- 8. 問題:訪問線程內的向量
- 9. 無效的跨線程訪問問題
- 10. 多線程數據庫訪問(.NET)
- 11. Facebook離線訪問.NET SDK
- 12. 訪問線程
- 13. c#顯示線程無效的跨線程訪問問題
- 14. 多線程.NET隊列問題
- 15. 在.net中的線程問題
- 16. 線程問題
- 17. 線程問題
- 18. 線程問題
- 19. 線程問題
- 20. 線程問題
- 21. 線程問題
- 22. 線程問題
- 23. 線程問題
- 24. 線程問題
- 25. 線程問題
- 26. IBM MQSeries從.NET訪問問題
- 27. 訪問Ruby線程
- 28. 從線程訪問
- 29. 課程訪問問題
- 30. 線程安全訪問數組和線程安全訪問
ReadWriterLockSlim爲+1。然而,我認爲OP在完全理解這個概念之前需要更多地熟悉多線程編程。至於你的第二點:你需要同步隊列,使問題複雜化。 – Thorarin 2010-01-22 14:18:10
真的夠了,你的回答爲這些概念提供了一個非常優秀的介紹(雖然我試圖在更新中採取不同的方式)。關於隊列,確實也必須同步,儘管有些情況下這種方法可以簡化事情而不是使事情複雜化! – 2010-01-22 14:24:35