2011-06-18 62 views
27

我一直在閱讀許多來源,the volatile keywordis not helpfulin multithreaded scenarios。但是,這種說法經常受到接受指針的原子操作函數的挑戰。如果volatile對於線程無用,爲什麼原子操作需要指向volatile數據的指針?

例如,在Mac OS X,我們有OSAtomic函數族:

SInt32 OSIncrementAtomic(volatile SInt32 *address); 
SInt32 OSDrecrementAtomic(volatile SInt32 *address); 
SInt32 OSAddAtomic(SInt32 amount, volatile SInt32 *address); 
// ... 

它似乎有在Windows上volatile關鍵字爲Interlocked操作類似的用法:

LONG __cdecl InterlockedIncrement(__inout LONG volatile *Addend); 
LONG __cdecl InterlockedDecrement(__inout LONG volatile *Addend); 

似乎在C++ 11中,原子類型具有volatile修飾符的方法,這必須以某種方式表示volatile關鍵字與atomicit具有某種關係年。

那麼,我錯過了什麼?爲什麼操作系統供應商和標準庫設計者堅持使用volatile關鍵字作爲線程目的,如果它沒有用的話?

+3

這並不是揮發性沒有用,更多的是揮發性數據不能保證原子操作 –

+3

不是一個愚蠢的,但與http://stackoverflow.com/questions/2479067/why-is-the-volatile- qualifier-used-through-out-stdatomic –

回答

17

突然對我說,我只是誤解了volatile*的意思。就像const*意味着指針不應該改變,volatile*意味着指針不應該被緩存在一個寄存器中。這是一個額外的限制條件,可以自由添加:儘管您可以將char*投射到const char*,但您可以將int*投射到volatile int*

因此,將volatile修飾符應用於pointees只是確保原子函數可以用於已有的volatile變量。對於非易失性變量,添加限定符是免費的。我的錯誤是將原型中關鍵字的存在解釋爲使用它的動機,而不是作爲使用它的人的便利。

+0

它也使'volatile'有用於防止您通過引用不是線程感知的函數意外傳遞共享變量。 –

1

好吧,關鍵字'volatile'可以確保編譯器每次在代碼中顯示變量時總是加載/存儲變量從/到內存的值。
這可以防止某些優化,例如該值只需加載一次寄存器,然後多次使用。
當您有多個線程可以修改線程之間的「共享」變量時,它非常有用。您必須確保始終將內存中的值加載/存儲到內存中,以檢查其值可能已被其他線程修改的值。如果不使用volatile,另一個線程可能沒有將新值寫入內存(但將其寫入寄存器或進行其他某種優化),並且第一個線程不會注意到任何值的更改。

在您的案例中,'volatile SInt32 * address'告訴編譯器,address指向的內存是任何源更改的主題。因此需要進行原子操作。

+0

您的答案也與[羅賓遜的博客文章]幾乎直接衝突(http://software.intel.com/zh-cn/blogs/2007/11/30/幾乎無用的多線程編程/),英特爾線程構建模塊的架構師,我在這個問題中鏈接了這個問題。 – zneak

+0

@zneak其實沒有。我從來沒有提到'代碼重新排序'會被阻止。它只是使得'SInt32 *地址'標記爲'volatile',因爲它可能會受到任何外部影響而改變。原子操作需要處理這個以維持原子性。因此,揮發性是標記此行爲的正確方法。 – iolo

22

揮發性也不是一無是處由多個線程共享訪問 - 這只是它並不一定足夠:

  • 它不一定能提供可能需要的內存屏障語義;
  • 它不提供原子訪問擔保(例如,如果揮發性對象比平臺的本地存儲器字大小)

此外,還應該注意的是,volatile預選賽上的指針參數在你的例子中的API實際上只增加了API接收指向volatile對象指針的能力 - 它並不要求指針指向實際的volatile對象。該標準允許非限定指針自動轉換爲合格的指針。在標準中沒有提供自動去另一種方式(合格的指向非合格指針)(編譯器通常允許它,但發出警告)。

例如,如果InterlockedIncrement()了原型爲:

LONG __cdecl InterlockedIncrement(__inout LONG *Addend); // not `volatile*` 

的API仍可實現正常工作的內部。但是,如果用戶有一個他想要傳遞給API的volatile對象,則需要進行強制轉換以防止編譯器發出警告。

由於(必要與否),這些API通常與volatile合格對象使用,加入volatile限定符到指針參數防止使用API​​時產生無用的診斷程序,並當所述API與用於損害沒什麼一個指向非易失性對象的指針。

+0

您閱讀我的回答並編輯您的匹配? ;) – zneak

+0

@zneak:只是一個巧合 - 我還沒有讀過其他答案。 –

+1

+1爲「必要但不足」 – jcoder

2

C++ 11對於volatile和非volatile變量都有原子。

如果編譯器內部函數將指針指向volatile int,這意味着您可以使用它即使變量是易失性的。它不會阻止您使用非volatile數據上的功能。

相關問題