2012-04-17 60 views
3

我有一個類型爲double的共享變量。這個變量將被兩個線程訪問。一個線程永遠只寫變量,而另一個線程永遠只讀變量。共享變量的爭用條件

我還有比賽條件嗎?如果是的話,C++中有沒有一種「簡單」的方式來實現原子訪問?如果讀取數比寫入數多得多,我如何有效地實現它?我是否需要將變量標記爲volatile

編輯:確定「讀取器」線程週期性地在批量數據上工作,並且新值的傳播不是時間敏感的。我可以聲明寫入器線程將寫入的另一個臨時變量,而不是實現複雜的互鎖,而我沒有很好的方法來測試。然後,當閱讀器完成一個批處理時,它可以將臨時值自動傳播到實際變量。那會是無競賽條件的嗎?

+0

@Jesse:許多編譯器不具有該頭的工作實現呢。 – 2012-04-17 22:38:19

+0

一個簡單的互斥體可以在這裏工作,以確保您正在閱讀良好的價值。當寫入線程正在使用變量時,互斥鎖將阻止對變量的訪問,並在讀取線程完成工作時解鎖它。在unix和windows上,實現都非常簡單。 – Chris911 2012-04-17 22:38:47

+0

@ Chris911:互斥鎖不必要的昂貴,可能會阻塞一個線程。有一個可以等待的實現。 – 2012-04-17 22:45:22

回答

8

是的,存在競態條件,因爲double變量在大多數處理器上不是原子的。

使用3個雙打(可能是一個有額外填充的數組,以避免錯誤的共享導致性能下降)。

一個由讀者擁有,一個由作者擁有,一個正在交出。要寫入:寫入寫入插槽,然後用寫入插槽的指針/索引與切換插槽的索引原子交換(例如用InterlockedExchange)。由於索引是指針大小或更小,只要變量正確對齊,原子交換就很容易。如果碰巧你的平臺提供了帶有和沒有記憶障礙的互鎖交換,那麼使用它。

要讀取:將讀取插槽的指針/索引與切換變量的索引進行原子交換。然後讀取讀取插槽。

你實際上也應該包括一個版本號,因爲讀線程將傾向於在最新和前一個槽之間反彈。閱讀時,請在交換前後讀取,然後使用版本較高的版本。

或者,在C++ 11中,只需使用std::atomic

警告:以上僅適用於單個作者/單個讀者(在此問題中的特定情況)。如果您有多個,請考慮讀寫器鎖定或類似的保護對變量的所有訪問。原始類型的

+0

請參閱編輯。 – 2012-04-17 22:47:01

+0

這會如何影響性能?我正在談論的是每秒發生數百次的讀取,而寫入可能會在一個小時內發生一次,即使完全一樣。 – 2012-04-17 23:05:41

+0

@Jakub:每秒數百次都不算什麼,你甚至不用擔心表現。如果它每秒數十萬次,那麼我們應該關注這個問題,我認爲我的建議仍然是最快的。 – 2012-04-17 23:37:18

0

你可能想看看這個,討論的讀/寫:

Are C++ Reads and Writes of an int Atomic?

+1

'double'的幫助如何,它通常與'int'的大小不一樣,並且具有不同的對齊要求? – 2012-04-17 22:36:49

+0

我認爲我想擺脫那個線程的關鍵是讀寫確實取決於您使用的系統的底層架構(並且我認爲如果正確對齊,某些多字節x64指令是原子的),則編譯器(即http://msdn.microsoft.com/en-us/library/aa290049%28VS.71%29.aspx)等等。正如你所指出的那樣,假設它不是原子的並且從那裏開始更安全。在本人的迴應中,我可以做得更清楚些。 – 2012-04-17 22:58:34