2014-11-27 14 views
15

Warum Programm mit diesem Code manchmal "2" druckt?C++ atomare Lese-/Schreib-Missverständnis

int main() { 
    std::atomic<int> a; 
    a = 0; 

    std::thread t1([&]{++a;}); 
    std::thread t2([&]{a++;}); 
    std::thread t3([&]{ 
     a = a.load() + 1; 
    }); 

    t1.join(); 
    t2.join(); 
    t3.join(); 

    if (a != 3) { 
     std::cout << "a: " << a << std::endl; 
    } 
} 

Ich habe gedacht std::atomic garantiert, dass alle Operationen atomar erfolgen wird also hier das Schreiben (Erhöhen) eine Speicherbarriere verwenden und wir werden immer 3 am Ende des Threads arbeiten. Ich habe den Code erforscht und herausgefunden, dass der Problemthread t3 ist, aber ich kann nicht verstehen, warum es falsch ist.

+8

'a = a.load() + 1 'ist nicht nur eine Operation. –

+0

++ a und a ++ wird nicht atomar durchgeführt. Vielleicht möchten Sie einen Blick auf http://en.cppreference.com/w/cpp/atomic/atomic/fetch_add werfen – Rush

+1

@Rush Was ist damit dann? http://en.cppreference.com/w/cpp/atomic/atomic/operator_arith @ JonathanPotter Ich habe so gedacht, aber wie funktioniert dann inkrementieren? Ich denke, es braucht zumindest einen zu lesen, oder? Also habe ich in 't3' versucht, es zu simulieren, aber es scheint, dass es falsch ist, wie @ ParkYoung-Bae sagte. Also ich denke das Problem ist, dass 't3' mit einem alten Wert überschreibt. –

Antwort

26

t3 führt im Gegensatz zu den beiden anderen Threads keine atomare Addition durch. Stattdessen lädt es a atomar, führt die Arithmetik (add 1) auf einem temporären aus, und speichert diesen neuen Wert atomar wieder zu a. Dies überschreibt a unabhängig von atomaren Operationen, die dazwischen geschehen könnten.

So können Sie das folgende Szenario haben:

  1. t1 oder t2 atomar Zuwachs a, die jetzt gleich 1.
  2. t3 atomar lädt 1.
  3. t1 oder t2 atomar Zuwachs a das ist jetzt gleich 2.
  4. t3 führt eine nicht-atomare hinzufügen auf dem previo Usly geladener Wert und speichert automatisch 2.