verhindern, dass std :: atomic überläuft

8

Ich habe einen atomaren Zähler ( std::atomic<uint32_t> count ), der nacheinander Werte für mehrere Threads inkrementiert.

%Vor%

Bevor ich my_val erhalte, möchte ich sicherstellen, dass das Inkrement nicht überläuft (zB: gehe zurück zu 0)

%Vor%

Ich denke, das ist eine naive Überprüfung, denn wenn die Überprüfung von zwei Threads durchgeführt wird, bevor der Zähler erhöht wird, erhält der zweite Thread, der erhöht werden soll, 0 zurück

%Vor%

Als solche denke ich, dass ich eine Operation CAS verwenden muss, um sicherzustellen, dass, wenn ich meinen Zähler inkrementiere, ich tatsächlich einen möglichen Überlauf verhindere.

Meine Fragen sind also:

  • Ist meine Implementierung korrekt?
  • Ist es so effizient wie es sein kann (genauer gesagt, muss ich zweimal gegen max prüfen)?

Mein Code (mit funktionierendem Exemplar) folgt:

%Vor%     
Steve Lorimer 28.05.2013, 05:47
quelle

3 Antworten

6

Ja, Sie müssen CAS operation verwenden.

%Vor%

Die Idee ist die folgende: sobald g_count == std::numeric_limits<uint16_t>::max() , get_next() function wird immer eine Ausnahme auslösen.

Schritte:

  1. Aktuellen Wert des Zählers abrufen
  2. Wenn es maximal ist, wirf eine Ausnahme (keine Nummern mehr verfügbar)
  3. Neuen Wert als Inkrement des aktuellen Werts erhalten
  4. Versuchen Sie, einen neuen Wert atomar zu setzen. Wenn wir es nicht einrichten konnten (es wurde bereits von einem anderen Thread ausgeführt), versuchen Sie es erneut.
Stas 28.05.2013, 06:37
quelle
2

Wenn Effizienz ein großes Problem ist, dann würde ich vorschlagen, nicht so streng auf den Scheck zu sein. Ich vermute, dass Überlauf bei normalem Gebrauch kein Problem ist, aber brauchen Sie wirklich den vollen 65K-Bereich (Ihr Beispiel verwendet uint16)?

Es wäre einfacher, wenn Sie von der Anzahl der Threads ausgehen, die Sie ausgeführt haben. Dies ist eine vernünftige Grenze, da kein Programm eine unbegrenzte Anzahl von Nebenläufigkeit hat. Wenn Sie also N threads haben, können Sie Ihr Überlauflimit einfach auf 65K - N reduzieren. Um zu vergleichen, ob Sie überlaufen, brauchen Sie kein CAS:

%Vor%

Dies erzeugt einen weichen Überlaufzustand. Wenn zwei Threads auf einmal kommen, werden beide potentiell passieren, aber das ist in Ordnung, da die count-Variable selbst niemals überläuft. Irgendwelche zukünftigen Ankünfte an diesem Punkt werden logisch überlaufen (bis der Zählwert wieder reduziert wird).

    
edA-qa mort-ora-y 28.05.2013 06:36
quelle
1

Es scheint mir, dass es immer noch eine Race-Bedingung gibt, bei der count momentan auf 0 gesetzt wird, so dass ein anderer Thread den 0-Wert sehen wird.

Nehmen Sie an, dass count bei std::numeric_limits<uint16_t>::max() ist und zwei Threads versuchen, den inkrementierten Wert zu erhalten. In dem Moment, in dem Thread 1 count.compare_exchange_strong(my_val, my_val + 1) ausführt, wird count auf 0 gesetzt, und das ist es, was Thread 2 sehen wird, wenn er code aufruft und get_val() beendet, bevor Thread 1 die Chance hat, count auf max() wiederherzustellen.

    
Michael Burr 28.05.2013 06:35
quelle

Tags und Links