La respuesta de Scheff describe cómo arreglar su código. Pensé que agregaría un poco de información sobre lo que realmente está sucediendo en este caso.
Compilé tu código en godbolt usando el nivel de optimización 1 ( -O1
). Su función se compila así:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
¿Entonces, Que esta pasando aquí? Primero, tenemos una comparación:cmp BYTE PTR finished[rip], 0
esto verifica si finished
es falso o no.
Si es no falsa (también conocido como verdadera) debemos salir del bucle en la primera ejecución. Esto logra jne .L4
que j umps cuando n ot e qual a la etiqueta.L4
cuando el valor de i
( 0
) se almacena en un registro para su uso posterior y se devuelve la función.
Si se Sin embargo, es falso, pasamos a
.L5:
jmp .L5
Este es un salto incondicional, para etiquetar .L5
que resulta ser el comando de salto en sí.
En otras palabras, el hilo se coloca en un bucle ocupado infinito.
Entonces, ¿por qué ha sucedido esto?
En lo que respecta al optimizador, los hilos están fuera de su alcance. Se supone que otros hilos no leen o escriben variables simultáneamente (porque eso sería una carrera de datos UB). Debe decirle que no puede optimizar los accesos de distancia. Aquí es donde entra la respuesta de Scheff. No me molestaré en repetirlo.
Debido a que no se le dice al optimizador que la finished
variable puede cambiar potencialmente durante la ejecución de la función, ve quefinished
la función en sí no la modifica y asume que es constante.
El código optimizado proporciona las dos rutas de código que resultarán de ingresar a la función con un valor bool constante; o ejecuta el bucle infinitamente, o el bucle nunca se ejecuta.
en -O0
el compilador (como se esperaba) no optimiza el cuerpo del bucle y la comparación:
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
por lo tanto, la función, cuando no se optimiza, funciona, la falta de atomicidad aquí no suele ser un problema, porque el código y el tipo de datos son simples. Probablemente lo peor con lo que podríamos encontrarnos aquí es un valor de i
uno a lo que debería ser.
Un sistema más complejo con estructuras de datos es mucho más probable que produzca datos corruptos o una ejecución incorrecta.