2014-11-21 16 views
14

Meine erste Frage ist: Gibt es eine Möglichkeit, auf die Mitglieder der Struktur in einem atomic<struct> Objekt zuzugreifen? Zum Beispiel habe ich den Compiler-Fehler:Missverständnis von Atomstrukturen und Zeigern

struct std::atomic<node>’ has no member named ‘data’ a.data = 0; 

in diesem Segment

struct node{ 
    int data; 
    node* next; 
}; 

int main(){ 
    atomic<node> a; 
    a.data = 0; 
} 

Ich kann einen temporären Knoten um ihn herum arbeiten wie so durch die Schaffung von:

atomic<node> a; 
    node temp; 
    temp.data = 0; 
    a.store(temp); 

aber dieses doesn Es scheint sehr elegant zu sein.

Die zweite Frage ist, was ist, wenn ich einen Zeiger auf ein atomares Objekt habe? Gibt es trotzdem einen direkten Zugriff auf die Mitglieder des Knotens? Offensichtlich kompiliert das Folgende nicht, wie würde ich dies ändern, um 0 im Wert des Knotens bei b zu speichern?

atomic<node> b = new node; 
b->data = 0; 

eine Lösung Dies habe ich gefunden, aber auch hier gibt es eine elegantere Möglichkeit, dies zu tun ??

atomic<node> *b; 
node temp; 
temp.data = 0; 
b->store(&temp); 

Und schließlich, was ist der Unterschied zwischen atomic<node*> und atomic<node>*

+0

Nein, es gibt nur eine begrenzte Anzahl von atomaren Operationen (laden, speichern, austauschen, ...) –

+1

'atomare ' erzwingt die atomare Aktualisierung des Zeigers (nicht das, worauf der Zeiger zeigt, nur der Zeiger). 'Atom *' ist ein Zeiger auf einen '' atomic , deren Absicht ist, atomare Aktualisierung des 'node' Objekts zu erzwingen. – Barry

+2

Wenn Sie eine Struktur wollen, in der Sie * beide * Elemente zusammen aktualisieren oder eine davon atomisch ändern können (ohne compare_exchange_weak für die gesamte Struktur), können Sie [eine Vereinigung einer atomaren Struktur und einer Struktur mit zwei atomaren Strukturen verwenden Mitglieder] (http://stackoverflow.com/questions/38984153/implement-aba-counter-with-c11-cas/38991835#38991835). (Wenn Sie einen C++ - Compiler verwenden, der garantiert, dass Sie ein Union-Mitglied schreiben und dann ein anderes lesen, ist das in Ordnung, wie in C99). Dies funktioniert tatsächlich (effizient) für Strukturen bis zu der maximalen Größe, die die Hardware cmpxchg kann, d. H. 16B auf x86-64. –

Antwort

9

this [workaround] doesn't seem very elegant.

std::atomic<T> nicht willkürlich Operationen atomar machen: nur das Laden und Speichern der Daten unterstützt wird. Das ist der Grund, warum Ihre "Workaround" eigentlich der Weg ist, mit atomaren Objekten umzugehen: Sie bereiten den neuen Wert node auf jede beliebige Art und Weise vor und setzen ihn dann atomar in eine atomic<node> Variable.

what if I have a pointer to an atomic object? Is there anyway to access the members of the node directly?

Zugriff auf den Inhalt eines Knotens über einen Zeiger nicht so gut atomar sein würde: da std::atomic<T> nur Laden garantieren kann und seinen Wert zu speichern Atom zu sein, ist es nicht lassen Sie Zugriff T ‚s Mitglieder ohne eine Herstellung explizite Kopie. Dies ist eine gute Sache, weil es verhindert, dass Leser des Codes einen falschen Eindruck bekommen, dass der Zugriff auf die Interna von T irgendwie atomar ist.

what is the difference between atomic<node*> and atomic<node>*

In den Tannen Fall speichert das Atom Objekt einen Zeiger, die atomar zugegriffen werden kann (das heißt Sie dieser Zeiger auf einen neuen Knoten atomar erneut Punkt kann). Im zweiten Fall speichert das atomare Objekt den Wert, auf den atomar zugegriffen werden kann, was bedeutet, dass Sie das gesamte node atomar lesen und schreiben können.

+1

Also, wenn ich eine Änderung an ein Mitglied machen wollte atomar passieren, soll ich das Mitglied atomare und nicht die Struktur machen? –

+1

@MattPennington genau!Wenn der Zugriff auf Daten innerhalb von 'node' atomar sein soll, würde Ihr Knoten wie folgt aussehen:' struct node {atomic data;}; ' – dasblinkenlight

+0

Solange Sie damit einverstanden sind, jeden zu belasten, der Ihre Struktur unter keinen Umständen benutzt ein Atom. Wenn Sie nur in bestimmten Kontexten um die Thread-Sicherheit besorgt sind, ist es möglicherweise besser, nur einen separaten Mutex zu verwenden. – dlf

2

Wenn Sie

atomic<node> a; 
node temp; // use a.load() to copy all the fields of a to temp 
temp.data = 0; 
a.store(temp); 

tun verlieren Sie den Wert nächsten Feld. Ich würde die vorgeschlagene Änderung machen. Wenn der Knoten ein einfacher Typ wäre, wie std :: atomic_int, denke ich, dass mit dem Operator "=" möglich gewesen wäre. Sonst nicht. Ich denke nicht, dass es einen anderen Workaround für Ihren Fall gibt.

And lastly, what is the difference between atomic < node* > and atomic < node > *?

Wenn Sie Atom < Knoten *> die auf getan Operationen die Adresse eines Knotens Objekt wird atomarer während im anderen Fall, dass Sie Speicher für das Atom-Objekt und die Operationen erfolgen auf zuweisen müssen Das tatsächliche Knotenobjekt wird atomar sein.

1

Beachten Sie, dass Ihre „Lösung“ enthält eine nicht-atomare Read-Modify-Write aller Mitglieder außer .data.

atomic<node> a; 

node temp = a.load(); 
temp.data = 0; 
a.store(temp); // steps on any changes to other member that happened after our load 

Wenn Sie eine Struktur wollen, wo Sie atomar alle Mitglieder zusammen oder getrennt atomar ändern einer von ihnen (ohne compare_exchange_weak auf die gesamte Struktur) aktualisieren können, können Sie a union of an atomic struct and a struct with two atomic members verwenden. Dies könnte für z.B. beide Zeiger in einer doppelt verknüpften Liste oder ein Zeiger + Zähler. Aktuelle Compiler sind schlecht darin, sogar ein Mitglied einer atomaren Struktur zu lesen, ohne etwas Langsames zu tun, wie CMPXCHG16B zum Laden der gesamten Struktur zu verwenden und dann nur ein Mitglied zu betrachten. (Das ist bei gcc6.2 sogar mit memory_order_relaxed der Fall).

Diese Vereinigung Hack funktioniert nur, wenn Sie einen C++ Compiler verwenden, der garantiert, dass ein Gewerkschaftsmitglied zu schreiben und dann noch eine Lesung in Ordnung ist, wie es in C99 ist.

Die für structs bis zur maximalen Größe arbeitet, kann die Hardware, also 16B auf x86-64 cmpxchg (wenn Sie -mcx16 in gcc zu aktivieren, CMPXCHG16B zu verwenden, die die erste Generation des K8-CPUs unterstützte nicht, es ist so nicht technische Basislinie x86-64).

Für größere Strukturen, atomic<the_whole_thing> wird nicht gesperrt sein, und das Lesen/Schreiben von Mitgliedern davon durch atomic<int> in einem anderen Union-Mitglied wird nicht sicher sein. Das Lesen ist jedoch immer noch in Ordnung.

Dies kann ein Durcheinander der Speicher-Bestellung Semantik machen, denn auch stark geordnete x86 kann reorder a narrow store with a wider load that fully contains it. Wenn Sie meistens nur Unteilbarkeit brauchen, es ist toll, aber das vollständige Objekt lesen (zum Beispiel während eines cmpxchg tun) im selben Thread, nur ein Mitglied schrieben erfordert eine MFENCE auf x86 auch für acquire/release Semantik. Sie werden immer Ihren eigenen Shop sehen, aber wenn andere Threads im selben Objekt gespeichert sind, können Sie Ihren Store nach dem Laden beobachten.