Schreiben einer (sich drehenden) Fadenbarriere unter Verwendung von C ++ 11 Atomics

8

Ich versuche, mich mit C ++ 11 Atomics vertraut zu machen, also habe ich versucht, eine Barriere-Klasse für Threads zu schreiben (bevor jemand sich darüber beschwert, dass er keine vorhandenen Klassen verwendet): Das ist mehr für das Lernen / Selbstverbessern als für irgendeinen wirklichen Bedarf ). Meine Klasse sieht grundsätzlich so aus:

%Vor%

Alle Mitglieder werden auf null initialisiert, mit Ausnahme von thread_count, das die entsprechende Anzahl enthält. Ich habe die Wartefunktion als

implementiert %Vor%

Wenn Sie jedoch versuchen, es mit zwei Threads zu verwenden ( thread_count ist 2), w \ u00e4hlt der erste Thread in die Wait-Schleife, aber der zweite Thread entriegelt die Barriere nicht (es scheint, dass es nicht einmal dazu kommt) int val = counter[idx].fetch_add(1); , aber ich bin mir nicht sicher, dass etwa Allerdings, wenn ich gcc Atom-Spezifika bin mit unter Verwendung volatile int anstatt std::atomic<int> und Schreiben wait wie folgt:.

%Vor%

es funktioniert gut. Aus meiner Sicht sollte es zwischen den beiden Versionen keine grundsätzlichen Unterschiede geben (mehr zu dem Punkt, wenn die Sekunde weniger wahrscheinlich funktionieren sollte). Welches der folgenden Szenarien gilt also?

  1. Ich hatte Glück mit der zweiten Implementierung und mein Algorithmus ist Mist
  2. Ich habe std::atomic nicht vollständig verstanden und es gibt ein Problem mit der ersten Variante (aber nicht der zweiten)
  3. Es sollte funktionieren, aber die experimentelle Implementierung für C ++ 11-Bibliotheken ist nicht so ausgereift, wie ich gehofft habe

Für den Datensatz verwende ich 32bit mingw mit gcc 4.6.1

Der aufrufende Code sieht so aus:

%Vor%

Da mingw nicht <thread> headers jet verwendet, verwende ich eine selbstgeschriebene Version für das, was im Grunde die entsprechenden pthread-Funktionen umschließt (bevor jemand fragt: Ja, es funktioniert ohne die Barriere, also sollte es kein Problem sein mit die Verpackung) Irgendwelche Einsichten würden geschätzt werden.

edit: Erklärung für den Algorithmus, um es klarer zu machen:

  • thread_count ist die Anzahl der Threads, die auf die Barriere warten sollen (wenn also thread_count Threads in der Barriere sind, können alle die Barriere verlassen).
  • lock wird auf eins gesetzt, wenn der erste (oder ein beliebiger) Thread die Barriere erreicht.
  • counter zählt, wie viele Threads innerhalb der Barriere liegen und wird für jeden Thread
  • einmal atomar inkrementiert
  • if counter>=thread_count Alle Threads befinden sich innerhalb der Barriere, also werden Zähler und Sperre auf Null zurückgesetzt
  • andernfalls wartet der Thread darauf, dass lock zu null wird
  • in dem nächsten Einsatz der Barriere verschiedenen Variablen ( counter , lock ) verwendet, noch keine Probleme sicherzustellen, dass es, wenn Gewinde auf dem ersten Einsatz der Barriere warten (zB sie verdrängt worden war, als die Barriere aufgehoben)

edit2: Ich habe jetzt getestet es gcc 4.5.1 unter Linux verwenden, in denen beide Versionen scheinen gut zu funktionieren, was zu einem Problem mit mingw des std::atomic zu verweisen scheint, aber ich bin immer noch nicht ganz überzeugt, da ein Blick in das Verzeichnis <atomic> Header revaled, dass die meisten Funktionen einfach die entsprechende gcc-atomare Bedeutung nennen, da es eigentlich keinen Unterschied zwischen den beiden Versionen geben sollte

    
Grizzly 13.11.2011, 22:29
quelle

7 Antworten

5

Es sieht unnötig kompliziert aus. Probieren Sie diese einfachere Version aus (gut, ich habe es nicht getestet, ich habe nur darüber nachgedacht :))):

%Vor%

BEARBEITEN: @Grizzy, ich kann keine Fehler in Ihrer ersten (C ++ 11) Version finden und ich habe es auch für hundert Millionen Syncs mit zwei Threads ausgeführt und es wird abgeschlossen. Ich habe es jedoch auf einem Dual-Socket / Quad-Core-GNU / Linux-Rechner laufen lassen, daher bin ich eher geneigt, Ihre Option 3 zu vermuten. - Die Bibliothek (oder vielmehr ihr Port zu win32) ist nicht ausgereift / p>     

chill 14.11.2011, 11:10
quelle
22

Ich habe keine Ahnung, ob dies hilfreich ist, aber der folgende Ausschnitt aus Herb Sutters Implementierung einer gleichzeitigen Queue verwendet einen Spinlock, der auf Atomics basiert:

%Vor%

Tatsächlich bietet der Standard einen speziellen Typ für diese Konstruktion, für den blockierungsfreie Operationen erforderlich sind, std::atomic_flag . Damit würde der kritische Abschnitt so aussehen:

%Vor%

(Sie können, wenn Sie möchten, die Speicherordnung dort akquirieren und freigeben.)

    
Kerrek SB 13.11.2011 22:49
quelle
5

Hier ist eine elegante Lösung aus dem Buch C ++ Concurrency in Aktion:. Practical Multithreading

%Vor%     
UncleHandsome 16.07.2014 09:31
quelle
4

Hier ist eine einfache Version von mir:

%Vor%

Verwendung: (aus std :: lock_guard Beispiel)

%Vor%     
ali_bahoo 16.11.2011 12:15
quelle
2

Ich weiß, dass der Thread ein bisschen alt ist, aber da es immer noch das erste Google-Ergebnis ist, wenn ich nur mit c ++ 11 nach einer Thread-Barriere suche, möchte ich eine Lösung vorstellen, die die ausgelasteten Wartezeiten mit der %Code%. Grundsätzlich ist es die Lösung von chill, aber statt der std::condition_variable -Schleife wird while und std::conditional_variable.wait() verwendet. In meinen Tests scheint es gut zu funktionieren.

%Vor%     
TeamTiffany 16.01.2013 14:07
quelle
2

Warum nicht std :: atomic_flag (aus C ++ 11) verwenden?

Ссылка

  

std :: atomic_flag ist ein atomarer boolescher Typ. Im Gegensatz zu allen Spezialisierungen   von std :: atomic ist es garantiert sperrfrei.

So würde ich meine Spinnfadenbarriere-Klasse schreiben:

%Vor%     
mchiasson 13.08.2014 12:52
quelle
1

Direkt aus der Dokumentation gestohlen

spinlock.h

%Vor%

usage.cpp

%Vor%     
Mark K Cowan 09.07.2015 21:20
quelle

Tags und Links