Ответ Шеффа описывает, как исправить ваш код. Я думал, что добавлю немного информации о том, что на самом деле происходит в этом случае.
Я скомпилировал ваш код в Godbolt, используя уровень оптимизации 1 ( -O1
). Ваша функция компилируется так:
func():
cmp BYTE PTR finished[rip], 0
jne .L4
.L5:
jmp .L5
.L4:
mov eax, 0
ret
Итак, что здесь происходит? Во-первых, у нас есть сравнение: cmp BYTE PTR finished[rip], 0
- это проверяет, finished
является ли оно ложным или нет.
Если это не ложь (иначе истина), мы должны выйти из цикла при первом запуске. Это достигается путем jne .L4
которого J umps при п OT е каче к этикетке , .L4
где значение i
( 0
) хранится в регистре для последующего использования и функция возвращает.
Если это является ложным , однако, мы переходим к
.L5:
jmp .L5
Это безусловный переход, для обозначения .L5
которого именно так и происходит сама команда перехода.
Другими словами, поток помещается в бесконечный цикл занятости.
Так почему же это произошло?
Что касается оптимизатора, потоки находятся за пределами его компетенции. Предполагается, что другие потоки не читают и не записывают переменные одновременно (потому что это будет гонка данных UB). Вы должны сказать ему, что он не может оптимизировать доступ. Здесь приходит ответ Шеффа. Я не буду его повторять.
Поскольку оптимизатору не сообщают, что finished
переменная может потенциально измениться во время выполнения функции, он видит, что finished
она не изменена самой функцией, и предполагает, что она постоянна.
Оптимизированный код обеспечивает два пути кода, которые будут получены при вводе функции с постоянным значением bool; либо он запускает цикл бесконечно, либо цикл никогда не запускается.
у -O0
компилятора (как и ожидалось) не оптимизируется тело цикла и сравнение отсюда:
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
поэтому функция, когда она неоптимизирована, работает, отсутствие атомарности здесь обычно не является проблемой, потому что код и тип данных просты. Вероятно, худшее, с чем мы могли бы здесь столкнуться, - это значение i
, которое на единицу меньше, чем должно быть.
Более сложная система со структурами данных может привести к повреждению данных или неправильному выполнению.