2012-12-13 92 views
3

我總是被告知要將鎖鎖定在多個線程可以訪問的變量上,我一直認爲這是因爲您想確保在使用之前寫入的值不會發生變化 即螺紋爲什麼危險?

mutex.lock() 
int a = sharedVar 
a = someComplexOperation(a) 
sharedVar = a 
mutex.unlock() 

而且這是有道理的,你會鎖定。但在其他情況下,我不明白爲什麼我不能逃避不使用Mutexes。

線程A:

sharedVar = someFunction() 

線程B:

localVar = sharedVar 

什麼可能出問題在這種情況下?特別是如果我不在乎線程B讀取線程A分配的任何特定值。

+0

我不是一個權威人士,但我猜測這是因爲你最終可能會碰撞。如果您在寫入時嘗試訪問變量,那麼您可能會遇到訪問衝突。 –

+0

@ Pow-Ian內存訪問始終是可串行化的。硬件負責以合理的方式處理這個問題。 –

+0

問題是一個變量可能包含一個你不期待的值。當你只讀時,這不是問題。當你開始寫和使用變量時,你不能再根據變量的值做出正確的決定。 – dmaij

回答

5

這取決於您使用的語言,任何框架和平臺的類型sharedVar。在很多情況下,將單個值分配給sharedVar可能需要多個指令,在這種情況下,您可能會讀取值的「半集」副本。

即使情況並非如此,分配也是原子分配,但如果沒有memory barrier,您可能看不到最新值。

+0

這不只是類型。它也依賴於語言(有些只有引用類型,其中賦值是近似全宇稱的)和內存模型(可以保證某些或所有類型的原子性)。 – delnan

+0

@delnan是的 - 類型/語言/框架/底層硬件/等 - 這一切都很重要。 –

+0

瞭解觀察部分更新值的術語是有用的:*撕裂*。在嘗試哄騙硬件以自動執行更新時,對齊非常重要。 –

0

這可能會出錯,因爲線程調度程序可能會掛起和恢復線程,所以您無法確定這些指令的執行順序。這或許也同樣是這個順序:

線程B:

localVar = sharedVar 

線程A:

sharedVar = someFunction() 

在這種情況下,localvar將是無效或0(或者在某些completeley意外的值一種不安全的語言),可能不是你想要的。

互斥鎖實際上不會解決這個問題。您提供的示例並不適合並行化。

1

主要問題是賦值運算符(運算符=在C++中)並不總是保證爲原子的(即使對於基元,內置類型也不是這樣)。用簡單的英語,這意味着分配可能需要超過一個時鐘週期才能完成。如果在該過程中線程被中斷,則該變量的當前值可能被破壞。

讓我建立了你的例子:

比方說sharedVar是一些物體operator=定義爲這樣:

object& operator=(const object& other) { 
    ready = false; 
    doStuff(other); 
    if (other.value == true) { 
     value = true; 
     doOtherStuff(); 
    } else { 
     value = false; 
    } 
    ready = true; 
    return *this; 
} 

如果從你的例子線程A在這個函數的中途中斷,線程B開始運行時就緒仍然是假的。這可能意味着當線程B試圖將其複製到局部變量中時,該對象只能被部分複製,或處於某種中間無效狀態。

對於這個特別討厭的例子,想象一下刪除節點被刪除的數據結構,然後在它被設置爲NULL之前中斷。

(有關不需要鎖(又名結構的一些更多的信息,是原子),here是另外一個問題,討論多一點有關。)

+0

即使在簡單類型的情況下,這是否也是一個問題? (如果共享var是一個浮點數) – Whyrusleeping

+0

對於一些簡單的類型,是的,這仍然是一個問題。 C++ 11引入了std :: atomic <>包裝類,它將爲許多基本類型提供有保證的原子訪問,但大多數情況下,你不能總是假設看起來是原子的東西將是原子的,除非編譯器保證就是這樣。 [Here](http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=469)是我發現的一篇文章,講述了一些在MSVC下是或不是原子的操作符,但是主要的收穫是基本整數數學通常是原子的,但其他的東西可能不是。 –

+0

[這裏](http://stackoverflow.com/questions/1292786/is-updating-double-operation-atomic)是另一個似乎有更詳細的信息,具體有關浮點類型的問題。 –

3

MSDN雜誌有不同的很好的解釋問題您可以在多線程代碼中遇到的問題:

  • 忘記同步
  • 不正確的粒度
  • 讀取和寫入撕裂
  • 無鎖的重新排序
  • 鎖定護航
  • 兩步舞
  • 優先級反轉

在你的問題中的代碼是特別容易讀/寫撕裂。但是,既沒有鎖也沒有內存障礙的代碼也受到無鎖定重新排序(其可能包括推測性寫入,其中線程B讀取線程A從未存儲的值),其中副作用對於第二秒可見線程的順序與它們在源代碼中的顯示順序不同。

它繼續描述一些已知的設計模式,它避免了這些問題:

  • 不變性
  • 純度
  • 隔離

的文章可以here

+0

感謝您閱讀本文! – Whyrusleeping

+0

@BenVoigt是否可以刷新文章鏈接? –