Ich habe einen atomaren Zähler ( std::atomic<uint32_t> count
), der nacheinander Werte für mehrere Threads inkrementiert.
Bevor ich my_val
erhalte, möchte ich sicherstellen, dass das Inkrement nicht überläuft (zB: gehe zurück zu 0)
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:
max
prüfen)? Mein Code (mit funktionierendem Exemplar) folgt:
%Vor% Ja, Sie müssen CAS
operation verwenden.
Die Idee ist die folgende: sobald g_count == std::numeric_limits<uint16_t>::max()
, get_next()
function wird immer eine Ausnahme auslösen.
Schritte:
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:
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).
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.