La réponse de Scheff décrit comment réparer votre code. J'ai pensé ajouter quelques informations sur ce qui se passe réellement dans ce cas.
J'ai compilé votre code sur godbolt en utilisant le niveau d'optimisation 1 ( -O1
). Votre fonction se compile comme suit:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Alors, que se passe-t-il ici? Tout d'abord, nous avons une comparaison: cmp BYTE PTR finished[rip], 0
- cela vérifie si finished
c'est faux ou non.
Si ce n'est pas faux (ou vrai), nous devons quitter la boucle lors de la première exécution. Ceci accompli par jne .L4
lequel j umps quand n ot e qual pour étiqueter .L4
où la valeur de i
( 0
) est stockée dans un registre pour une utilisation ultérieure et la fonction retourne.
S'il est faux cependant, nous passons à
.L5:
jmp .L5
Il s'agit d'un saut inconditionnel, à étiqueter .L5
qui se trouve être la commande de saut elle-même.
En d'autres termes, le thread est placé dans une boucle occupée infinie.
Alors pourquoi est-ce arrivé?
En ce qui concerne l'optimiseur, les threads sont en dehors de sa compétence. Il suppose que les autres threads ne lisent ni n'écrivent les variables simultanément (car ce serait l'UB de la course aux données). Vous devez lui dire qu'il ne peut pas optimiser les accès. C'est là que la réponse de Scheff entre en jeu. Je ne prendrai pas la peine de le répéter.
Étant donné que l'optimiseur n'est pas informé que la finished
variable peut potentiellement changer pendant l'exécution de la fonction, il voit qu'il finished
n'est pas modifié par la fonction elle-même et suppose qu'elle est constante.
Le code optimisé fournit les deux chemins de code qui résulteront de l'entrée de la fonction avec une valeur booléenne constante; soit il exécute la boucle à l'infini, soit la boucle n'est jamais exécutée.
au -O0
compilateur (comme prévu) n'optimise pas le corps de la boucle et la comparaison:
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
par conséquent, la fonction, lorsqu'elle n'est pas optimisée, fonctionne, le manque d'atomicité ici n'est généralement pas un problème, car le code et le type de données sont simples. Le pire que nous pourrions rencontrer ici est probablement une valeur i
qui est inférieure de un à ce qu'elle devrait être.
Un système plus complexe avec des structures de données est beaucoup plus susceptible d'entraîner des données corrompues ou une exécution incorrecte.