Гонки данных в C ++ - неопределенное поведение.
Теперь вы можете подумать, что это означает "ну, я в порядке, когда они пропустили чтение, они в конце концов получат его". Но UB - это то, что компилятор может предположить, что не произойдет .
Тот факт, что вы читаете threadRun
без блокировки или другой синхронизации, означает, что компилятор может, может и должен предполагать, что никто в любом другом потоке не изменит threadRun
.
Таким образом, код можно оптимизировать до:
if(threadRun)
while(true)
т. Е. Компилятор может прочитать threadRun
один раз, а затем не беспокоиться о его повторном чтении, если он может доказать, что никто не может изменить его значение определенным образом.
Теперь, если компилятор не может доказать это, он вместо этого должен прочитать threadRun
. Таким образом, ваш UB ведет себя так, как будто он «должен» работать. А на некоторых платформах само оборудование кэшировало threadRun
в кеше для каждого процессора (или потока), и тот факт, что другой поток записывал в него, не отражается в вашем рабочем потоке в течение некоторого неизвестного периода времени (возможно, навсегда).
Если пойти дальше, умный компилятор может заметить, что вы читаете threadRun
без синхронизации. Затем он может использовать это, чтобы доказать, что никто, с синхронизацией или без нее, не может писать в threadRun
в другом потоке.
Как только он докажет это, он может исключить проверку threadRun
в мьютексе.
Не выполняйте неопределенное поведение, если только вы не хотите проверять сборку, которую ваша программа производит с этого момента и до конца времени, каждый раз, когда вы ее компилируете, даже если она «работает» или если выигрыш огромен и стоит риска.
Заменить threadRun
на std::atomic_bool
. В худшем случае вы потеряете немного производительности и получите правильность.