La risposta di Scheff descrive come riparare il tuo codice. Ho pensato di aggiungere alcune informazioni su ciò che sta realmente accadendo in questo caso.
Ho compilato il tuo codice su godbolt usando il livello di ottimizzazione 1 ( -O1
). La tua funzione si compila così:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Quindi, cosa sta succedendo qui? Innanzitutto, abbiamo un confronto: cmp BYTE PTR finished[rip], 0
- questo controlla se finished
è falso o no.
Se è non è falso (aka vero) dobbiamo uscire dal ciclo alla prima esecuzione. Ciò ottiene jne .L4
che j UMPS quando n ot e qual all'etichetta .L4
cui il valore di i
( 0
) è memorizzato in un registro per un uso successivo e restituisce la funzione.
Se è falso, tuttavia, ci spostiamo a
.L5:
jmp .L5
Questo è un salto incondizionato, per etichettare .L5
che è proprio il comando di salto stesso.
In altre parole, il thread viene inserito in un loop occupato infinito.
Allora perché è successo?
Per quanto riguarda l'ottimizzatore, i thread non rientrano nella sua sfera di competenza. Presuppone che altri thread non leggano o scrivano variabili contemporaneamente (perché sarebbe UB data-race). Devi dirlo che non può ottimizzare gli accessi. È qui che arriva la risposta di Scheff. Non mi preoccuperò di ripeterlo.
Poiché all'ottimizzatore non viene detto che la finished
variabile può potenzialmente cambiare durante l'esecuzione della funzione, vede che finished
non viene modificata dalla funzione stessa e presuppone che sia costante.
Il codice ottimizzato fornisce i due percorsi del codice che risulteranno dall'inserimento della funzione con un valore bool costante; o esegue il ciclo all'infinito o il ciclo non viene mai eseguito.
al -O0
compilatore (come previsto) non ottimizza il corpo del loop e il confronto via:
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
pertanto la funzione, quando non ottimizzata funziona, la mancanza di atomicità qui non è in genere un problema, poiché il codice e il tipo di dati sono semplici. Probabilmente il peggio che potremmo incontrare qui è un valore i
che è fuori da quello che dovrebbe essere.
Un sistema più complesso con strutture di dati ha molte più probabilità di provocare dati danneggiati o un'esecuzione impropria.