Scheffs Antwort beschreibt, wie Sie Ihren Code reparieren können. Ich dachte, ich würde ein paar Informationen darüber hinzufügen, was in diesem Fall tatsächlich passiert.
Ich habe Ihren Code bei Godbolt mit Optimierungsstufe 1 ( -O1
) kompiliert . Ihre Funktion kompiliert wie folgt:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Also, was passiert hier? Zuerst haben wir einen Vergleich: cmp BYTE PTR finished[rip], 0
- Dies prüft, ob finished
falsch ist oder nicht.
Wenn es nicht falsch ist (auch bekannt als wahr), sollten wir die Schleife beim ersten Lauf verlassen. Dies erreicht durchjne .L4
die j UMPS wenn n ot e Qual zu Label .L4
wobei der Wert von i
( 0
) für eine spätere Verwendung in einem Register gespeichert wird , und die Funktion kehrt zurück.
Wenn es jedoch falsch ist, gehen wir zu
.L5:
jmp .L5
Dies ist ein bedingungsloser Sprung zum Beschriften, .L5
der zufällig der Sprungbefehl selbst ist.
Mit anderen Worten, der Thread wird in eine Endlos-Besetzt-Schleife gebracht.
Warum ist das passiert?
Für den Optimierer liegen Threads außerhalb seines Zuständigkeitsbereichs. Es wird davon ausgegangen, dass andere Threads nicht gleichzeitig Variablen lesen oder schreiben (da dies ein Datenrassen-UB wäre). Sie müssen ihm mitteilen, dass die Zugriffe nicht optimiert werden können. Hier kommt Scheffs Antwort ins Spiel. Ich werde mich nicht die Mühe machen, ihn zu wiederholen.
Da dem Optimierer nicht mitgeteilt wird, dass sich die finished
Variable möglicherweise während der Ausführung der Funktion ändern kann, sieht er, finished
dass sie von der Funktion selbst nicht geändert wird, und geht davon aus, dass sie konstant ist.
Der optimierte Code stellt die beiden Codepfade bereit, die sich aus der Eingabe der Funktion mit einem konstanten Bool-Wert ergeben. Entweder wird die Schleife unendlich ausgeführt, oder die Schleife wird nie ausgeführt.
beim -O0
Compiler (wie erwartet) wird der Schleifenkörper und Vergleich nicht weg optimiert:
func():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], 0
.L148:
movzx eax, BYTE PTR finished[rip]
test al, al
jne .L147
add QWORD PTR [rbp-8], 1
jmp .L148
.L147:
mov rax, QWORD PTR [rbp-8]
pop rbp
ret
Daher funktioniert die Funktion, wenn sie nicht optimiert ist, ist der Mangel an Atomizität hier normalerweise kein Problem, da der Code und der Datentyp einfach sind. Wahrscheinlich ist das Schlimmste, auf das wir hier stoßen könnten, ein Wert, der i
um eins von dem abweicht, was er sein sollte.
Ein komplexeres System mit Datenstrukturen führt mit größerer Wahrscheinlichkeit zu beschädigten Daten oder einer fehlerhaften Ausführung.