2014-09-03 388 views
6

我感到困惑的C++ 11的原子操作,當增加原子變量並將其賦值給其他值時,C++ 11是原子操作嗎?

我知道原子變量自我增值是原子操作,

,但我用的是分配給其他值,只是懷疑。

代碼就像:

//.... 
static std::atomic<int> i; // global variable 
//.... 
// in the thread 
int id = ++i; 
使用在不同的線程分配的情況下

,是id獨特的價值?

測試代碼:

#include <thread> 
#include <mutex> 
#include <atomic> 
#include <iostream> 

class A { 
public: 
    static int idGenerator; 
    static std::mutex m; 
    A() { 
     // i know this operation will keep the id_ is unique 
     std::lock_guard<std::mutex> lock(m); 
     id_ = ++idGenerator; 
    } 
    void F(std::string name) { 
     std::cout << name << " " << id_ << std::endl; 
    } 
private: 
    int id_; 
}; 
int A::idGenerator = 0; 
std::mutex A::m; 

class B { 
public: 
    static int idGenerator; 
    B() { 
     // after self increment, is the assignment atomic? 
     id_ = (++idGenerator); 
    } 
    void F(std::string name) { 
     std::cout << name << " " << id_.load() << std::endl; 
    } 
private: 
    std::atomic<int> id_; 
}; 
int B::idGenerator = 0; 


void funcA() { 
    A a2; 
    a2.F("a2"); 
} 

void funcB() { 
    B b2; 
    b2.F("b2"); 
} 

int main() { 
    A a1; 
    B b1; 
    std::thread t1(&funcA); 
    std::thread t2(&funcB); 
    a1.F("a1"); 
    b1.F("b1"); 

    t1.join(); 
    t2.join(); 
    return 0; 
} 

有三個線程,

類使用lock_guard保持獨特。

B級只使用原子操作,並賦給變量

+0

參見API'的std ::原子:: fetch_add'覆蓋在原子單元的兩個操作。 – 2014-09-03 04:40:31

+0

對原子變量(或增量操作)的賦值是很原子的。對任何其他變量的賦值不保證是原子的。但是,爲了使其「線程安全」,在併發訪問的所有場景中,單獨使用原子性是不夠的。此外,在'B'類看你的代碼,看起來你想讓靜態成員變量idGenerator原子,而不是成員變量'id_'。 – CouchDeveloper 2014-09-03 05:46:59

+0

我讀了crtmpserver代碼,並且每個連接只有一個iohandler類,iohandler的id由靜態的generateId生成。 crtmpserver是Single Process,如果我嘗試添加多線程支持,那麼id應該保持與前面一樣 – 2014-09-03 08:37:17

回答

0

kaka_ace,

不幸的是,你所提供的情況下,它是不是原子。

這裏的原因預先遞增操作是原子,看生成的彙編:

add %l0,1,%l0 

(可能會有所不同取決於所使用的組件一點點)

但僅此而已。 1操作。這就是爲什麼它是原子。

當你分配一個預增量的局部變量,這至少兩個指令:

add %l0,1,%l0 
st l0, [%fp-4] 

,用於產生至少兩個指令,因此不再原子。

如果您有任何問題,請讓我知道!

+1

'add%l0,1,%l0'顯示代碼使用CPU寄存器來存儲原子值 - 這不會是無論如何,從其他線程修改,所以是有效的原子。有意思/有意義的情況是,原子變量可能被另一個線程修改的地方 - 然後它是修改內存中原子值*的代碼,這是相關的 - C++標準是否需要一個操作碼來管理以原子方式增加內存地址並避免第二次讀取以獲得問題代碼中「id」的值。 – 2014-09-03 04:44:44

+0

@TonyD,啊我看到了;謝謝你的澄清和你的回答!喜歡學習新東西,看到並理解線程間共享內存中讀取原子性的重要性和上下文。 – 2014-09-03 16:04:39

+0

不用擔心 - 很高興看到一些Sparc大會!實際上 - 我上面的評論談到需要一個操作碼,但這是誤導性的,因爲在沒有這種支持的CPU上,原子操作可能會倒退在互斥體上並涉及許多機器操作碼。乾杯。 – 2014-09-03 16:28:20

7

的單位遞增函數的規範給出了至關重要的洞察他們的行爲 - 積分T類型從http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith

T operator++(); 
T operator++() volatile; 
T operator++(int); 
T operator++(int) volatile; 

通知他們返回T的價值,從來沒有從預返回通常T& -增量。出於這個原因,增量後值的「讀」不是第二個不同的操作,並且是原子增量操作本身的一部分。

另請參閱上述鏈接頁面上的「返回值」和「註釋」文本。在使用

+1

謝謝!!!,我使用visual studio 2013來調試代碼,它調用atomic_fetch_add來保持原子。 – 2014-09-03 08:25:34

+0

很有參考價值,謝謝! – 2014-09-03 16:05:02

2
static std::atomic<int> i; // global variable 
// in the thread 
int id = ++i; 

在不同的線程分配,是ID獨特的價值?

是。 C++原子變量確保++i將以原子方式進行評估,因此不同線程上的每個值id都是唯一的。

表達式id = ++i;按照以下步驟執行。

  1. 原子增量i和子表達式(++i)在增量值後被評估。
  2. 將「評估值」分配給id。 (該步驟是非原子)