2011-02-09 19 views
1

所以我在SO和其他地方閱讀了大量關於共享變量,多線程和易失性主題的文章。基本類型的多線程和運算符++

如果考慮下面的代碼:

class C { 
    int x; 

public: 
    C() : x(0) { } 

    void Operation() { 
     AcquireMutex(); 
     ++x; 
     ReleaseMutex(); 
    } 
}; 

現在,如果我已經明白了一切,到目前爲止我讀過,這將是更新x以正確的方式,正確的?一個正確的編譯器不會對代碼重新排序,在調用AcquireMutex()之前緩存x的值,對吧?

我一直有一個習慣,用volatile來標記這樣的變量。恐龍在地上漫遊時,我回到學校的東西,從來沒有真正反映過它。在主題閱讀文章後,又好像我已經浪費了我生命中的打字了幾分鐘(對於這些類型的用途)無用的關鍵詞......

UPDATE: 好了,如果我改變Operation()此相反:

void Operation() { 
    AcquireMutex(); 
    ++x; 
    ReleaseMutex(); 
    AcquireMutex(); 
    ++x; 
    ReleaseMutex(); 
} 

現在,讓我們忽略了使用互斥的,並且內部函數如InterlockedIncrement(),或什麼的。除了我的觀點之外,這種說法還不錯

如果x不是標記爲volatile,請問上面的代碼是線程安全的嗎?難道編譯器決定在第一次遞增之後將x的最後一個值保存在一個寄存器中,然後只增加寄存器的值,並將其存儲在內存中的最後一個增量處?如果是這種情況,那麼上面的代碼不是線程安全的。是什麼賦予了?編譯器是否會假定在調用任何函數之後,所有緩存的變量都被認爲是「髒的」,從而迫使編譯器發出讀取操作?

+0

嘗試在這種情況下使用互鎖操作,至少嘗試使用CRITICAL_SECTION,因爲它比Windows上的互斥量輕。可以設計跨平臺代碼[tbb :: atomic](http://threadingbuildingblocks.org/files/documentation/a00117.html)。 –

回答

2

volatile沒有提到原子性。它的目的是防止緩存不應該被緩存的內存位置(例如,硬件設備DMA端口)。(編輯:該措辭參考了生成的代碼的「緩存」。例如,一個非volatile變量可能會從內存中讀取,然後無限期地保存在寄存器中.Arkadiy在下面的評論中提供了更精確的定義。)

正如其他人所指出的,C或C++中的任何操作都不能保證是原子操作。您可以根據需要自行管理互斥或其他警衛。

+2

更準確地說,「volatile」告訴編譯器該位置的值可以在編譯代碼之外改變,所以任何依賴於相同值的優化都不能被應用。 – Arkadiy

+0

對'sig_atomic_t'變量的操作是保證原子的,至少就信號而言。它並不(必然)意味着線程方面的任何事情,但爲了防萬一有人有這種感覺,「我確定我已經看到了關於C中的一個原子類型的東西。」 –

+0

@Jerry,'sig_atomic_t'爲特定大小的整數變量提供原子**訪問** - 對這些變量的單次讀取或寫入操作不會被信號中斷。它並不能保證這些變量的**操作**是原子的。例如,一個'++'操作表示兩次訪問 - 一次讀取和一次寫入。 –

1

如果您使用的是Windows,使用Visual Studio,你可以嘗試用所謂的內部函數:

#include <intrin.h> 
class C { 
    int x; 

public: 
    C() : x(0) { } 

    void Operation() { 
     _InterlockedIncrement(&x); 
    } 
}; 

More on compiler intrinsics. 不知道有其他的操作系統,但我敢肯定,也有內在。

2

我不太確定馬丁是對的。看看這個:

InterlockedIncrement Function

如果32位遞增是原子的,爲什麼需要InterlockedIncremenet

這就是說,你不應該使用互斥這種東西,這是一個巨大的浪費。使用CPU內部函數,如win32 api中的Interlocked*函數(及其他編譯器庫中的等價函數)。

+1

馬丁聯繫的文章顯然是錯誤的。作者對底層硬件做了很多假設。編譯器可以爲特定的CPU生成「單線程」並不一定是真的。即使它生成一個班輪,它仍然可能不是線程安全的。單線程可能會生成一個提取/增量/存儲週期,在具有多個CPU /內核的系統中不會是線程安全的。 –

+0

互斥體引用只是用於鎖定/釋放win32臨界區(這是我的生產代碼中的)的「僞代碼」。我認爲這些被稱爲「自旋鎖」。 –

+0

仍然過分,儘可能使用原子指令。 – Blindy