2011-01-07 70 views
48

注意:我真的不是很擅長Multithreaded編程,但是我現在的項目讓我這樣做,所以我試圖讓我的頭腦圍繞什麼是線程安全的,什麼不是。++運算符線程安全嗎?

我正在閱讀Eric Lippert的awesome answers on what ++i does之一。他說,這是真正發生的事情:

  1. 計算x以產生變量的變量
  2. 值將被複制到一個臨時位置
  3. 臨時值遞增產生一個新的值(不覆蓋臨時!)
  4. 新的值存儲在操作的可變
  5. 結果是新的值

這讓我想,如果兩個線程在哪裏調用++我會怎樣?如果當第二個線程在步驟2時第一個線程在步驟3.(意味着如果在第一個線程將新值存儲在變量中之前第二個線程將值複製到臨時位置,該怎麼辦?)

如果發生這種情況,似乎兩個線程只會增加i而不是兩次。 (除非整個事情在lock。)

+6

這裏是[另一個Eric](http://blog.decarufel.net/2009/02/why-operator-is-not-thread-safe.html),它解釋了爲什麼`++`操作符不是線程安全。關鍵是CPU不能直接在內存中進行數學運算 - 它必須先將內存中的值加載到寄存器中;這造成了競爭條件的可能性。使用多線程編程時需要注意的一點是,一般情況下,除非文檔*明確地告訴你它們是否是這樣,否則應該假設事情不是**線程安全的。 – 2011-01-07 17:23:42

回答

69

正如其他答案指出的,不,++不是「線程安全」。

當您瞭解多線程及其危害時,我認爲有助於您開始非常精確地描述「線程安全」的含義,因爲不同的用戶意味着不同的東西。本質上,您所關心的線程安全方面是操作是否爲原子或不。 「原子」操作是當被另一個線程中斷時,保證不會中途完成。 (有很多其他線程問題與原子性無關,但可能仍然落在某些人對線程安全的定義之下,例如,給定兩個線程,每個線程變異一個變量,兩個線程分別讀取變量,兩個讀者是否保證同意訂單其中另外兩個線程發生了突變?如果你的邏輯依賴於它,那麼即使每次讀寫都是原子的,你也有一個非常難以處理的線程安全問題。 )

在C#中,幾乎沒有什麼是保證是原子的。簡言之:

  • 讀取32位整數或浮點
  • 讀取參考
  • 寫一個32位整數或浮點
  • 寫入參考

保證是原子的(見詳細說明如下)

特別是讀寫64位整數或浮點數是不是保證是原子的。如果你說:

C.x = 0xDEADBEEF00000000; 

一個線程,

C.x = 0x000000000BADF00D; 

在另一個線程,則有可能到第三線程:

Console.WriteLine(C.x); 

有寫出來0xDEADBEEF0BADF00D,即使邏輯上該變量從未保留該值。 C#語言保留寫作的權利,等同於一次一個地寫入兩個整數,實踐中一些芯片確實以這種方式實現。第一次寫入後的線程切換可能會導致讀者讀取意外的內容。

它的長短是:不要分享什麼東西兩個線程之間沒有鎖定的東西。滿足時鎖只是緩慢的;如果因爲競爭鎖定而出現性能問題,則可修復造成鎖定的任何體系結構缺陷。如果鎖不競爭並且速度太慢,那麼只有在這種情況下才應該考慮採用危險的低鎖技術。

這裏使用的常見低鎖定技術當然是調用Threading.Interlocked.Increment,它以保證爲原子的方式增加整數。 (但是請注意,它仍然不能保證如果兩個線程在不同時間執行兩個不同變量的互鎖遞增,而其他線程正在嘗試確定哪個增量「首先」發生時會發生什麼事情,C#不保證所有線程都可以看到一致的事件順序。)

+0

@Eric:你知道是否有辦法讓所有東西都成爲開發環境/平臺或語言的原子嗎?我不確定這種技術是否適用於並行編程。但我不知道如何才能使某些東西成爲原子。也許這是因爲有東西需要開銷才能做到原子性? – 2011-01-07 19:39:48

28

不,它不是。

您提出的分析是非常正確的 - 如果++(和--)運算符沒有使用適當的鎖定語義,則它們容易受到競爭條件的影響。

這些是很難得到正確的,但幸運的是,首創置業擁有這些特定情況下,一個現成的解決方案:

您應該使用Interlocked.Increment如果你想要一個單位遞增操作。在Interlocked類中還定義了一個Decrement和其他幾個有用的原子操作。

遞增指定的變量並將結果存儲爲原子操作。

7

沒有,i++看起來小巧,但它確實是只爲i = i + 1速記和以這種形式更容易地看到,它涉及到讀取和的「我」的寫操作。沒有鎖定它根本不是線程安全的。

2個其他參數是:

  • '++」沒有被定義爲線程安全的
  • Interlocked.Increment (ref int x)

線程安全的存在是稀有和昂貴,這將是在可用時明確標示。