2015-06-10 9 views
17

Ich habe eine Klasse, die ich wan es in verschiedenen Threads zu verwenden, und ich denke, ich in der Lage sein kann std::atomic wie diese verwenden:wie std verwenden :: Atom <>

classs A 
{ 
    int x; 

public: 
    A() 
    { 
     x=0; 
    } 

    void Add() 
    { 
     x++; 
    } 

    void Sub() 
    { 
     x--; 
    }  
}; 

und in meinem Code :

std::atomic<A> a; 

und in unterschiedlichen Gewinde:

a.Add(); 

und

a.Sub(); 

aber wenn ich Fehler bekomme, ist a.Add() nicht bekannt. Wie kann ich das erreichen?

Gibt es einen besseren Weg, dies zu tun?

Edit 1

Bitte beachten Sie, dass es sich um eine Probe Beispiel ist, und das, was ich will, ist, um sicherzustellen, dass der Zugang zu der Klasse A THREAD ist, so kann ich nicht

std::atomic<int> x; 

verwende Wie kann ich Machen Sie eine Klasse thread sicher mit std :: atomic?

+2

Werfen Sie einen Blick auf Tutorial http://baptiste-wicht.com/posts/2012/07/c11-concurrency-tutorial-part-4-atomic-type.html. – user1929959

Antwort

4

Deklarieren Sie die Klasse Mitglied x als atomar, dann haben Sie nicht das Objekt als Atom

class A 
{ 
    std::atomic<int> x; 
} 
+1

@mans Sie können sicher auf verschiedene Elemente eines 'std :: vector' aus mehreren Threads zugreifen, solange Sie nicht auf dasselbe Element von mehreren Threads aus zugreifen. Das ist eine Garantie für die Standardbibliothek. – Morwenn

+1

@Morwenn Aber es scheint, dass ich nicht schreiben kann. Wenn ich in einem Thread lese und in einen anderen Thread schreibe, stürzt das System ab, liege ich falsch? – mans

24

Sie müssen die x Attribut atomar, und nicht Ihre ganze Klasse machen, um zu erklären, wie folgt:

class A 
{ 
    std::atomic<int> x; 

    public: 
     A() { 
     x=0; 
     } 
     void Add() { 
     x++; 
     } 
     void Sub() { 
     x--; 
     }  
}; 

Der Fehler, den Sie in Sie Original-Code erhalten, ist völlig normal: es gibt keine std::atomic<A>::Add Methode (siehe here), wenn Sie eine Spezialisierung für std::atomic<A> bieten.

Bezug deine Bearbeitung: Sie nicht auf magische Weise können Ihre class A Thread-sicher machen, indem es als Template-Argument von std::atomic verwenden. Um es threadsicher zu machen, können Sie seine Attribute atomar machen (wie oben vorgeschlagen und sofern die Standardbibliothek eine Spezialisierung dafür bietet) oder Mutexe verwenden, um Ihre Ressourcen selbst zu sperren. Siehe die mutex Kopfzeile. Zum Beispiel:

class A 
{ 
    std::atomic<int>  x; 
    std::vector<int>  v; 
    std::mutex   mtx; 

    void Add() { 
    x++; 
    } 
    void Sub() { 
    x--; 
    } 

    /* Example method to protect a vector */ 
    void complexMethod() { 
    mtx.lock(); 

    // Do whatever complex operation you need here 
    // - access element 
    // - erase element 
    // - etc ... 

    mtx.unlock(); 
    } 

    /* 
    ** Another example using std::lock_guard, as suggested in comments 
    ** if you don't need to manually manipulate the mutex 
    */ 
    void complexMethod2() { 
    std::lock_guard<std::mutex> guard(mtx); 

    // access, erase, add elements ... 
    } 

}; 
+8

Ich würde 'std :: lock_guard' anstelle von manuell Sperren und Freigeben. –

+0

@LukeB. Bearbeitet, danke für den Vorschlag. – Unda

+1

@ LukeB.Verwandt die Idee hinter lock_guard, dass die Destruktoren der automatischen Variablen aufgerufen werden, wenn das Scope verlassen wird, um die Sperre aufzuheben, genau wie bei RAII? – Virus721

3

Der . Operator kann auf einem Objekt verwendet werden, um seine Klasse Member-Funktion aufrufen, nicht irgendein Mitglied Funktion der anderen Klasse (sofern Sie nicht ausdrücklich den Code, der Art und Weise schreiben).

std::atomic<A> a ; 
a.Add(); // Here, a does not know what Add() is (a member function of the type parameter) 
     // It tries to call Add() method of its own class i.e. std::atomic 
     // But std::atomic has no method names Add or Sub 

Als Antwort von @ivanw erwähnt, machen std::atomic<int> ein Mitglied Ihrer Klasse statt und dann verwenden.

Hier ist ein weiteres Beispiel:

template <typename T> class A 
{}; 

class B { public: void hello() { std::cout << "HELLO!!!"; } }; 

A<B> a ; 
a.hello(); // This statement means that call a's hello member function 
      // But the typeof(a) which is A does not have such a function 
      // Hence it will be an error. 
0

Ich denke, das Problem mit den oben genannten Antworten ist, dass sie nicht erklären, was ich denke, ist, auf einem Minimum, eine Unklarheit in der Frage, und höchstwahrscheinlich, ein häufiger Entwicklungsfehler.

Sie können ein Objekt nicht "atomar" machen, da das Intervall zwischen zwei Funktionen (zuerst "x lesen" und dann später "x schreiben") ein Rennen mit anderen Verwendungen verursacht. Wenn Sie der Meinung sind, dass Sie ein "atomares" Objekt benötigen, müssen Sie die API- und Memberfunktionen, die jetzt verfügbar gemacht werden sollen, sorgfältig entwerfen, um Aktualisierungen für das Objekt zu beginnen und zu übernehmen.

Wenn alles, was Sie mit "atomic" meinen, ist "das Objekt seinen internen Zustand nicht beschädigt", dann können Sie dies erreichen durch std::atomic<> für einzelne Plain-Old-Datentypen, die keine Invariante zwischen ihnen haben (ein doesn ' t hängt von b ab, aber Sie benötigen eine Sperre für abhängige Regeln, die Sie erzwingen müssen.