Официальный ответ: если вы разделяете (не атомарную) логическую переменную между потоками и не сериализуете доступ к этой переменной (например, с мьютексом), то вы вызвали неопределенное поведение, и программа бесплатна делать все, что угодно (работать как положено, работать как-то иначе, сбой, кража денег из кошелька и т. д.).
Более практичный ответ: на современной многоядерной машине каждое ядро имеет свои собственные регистры и отдельный кэш L1, и поэтому, если поток, работающий на ядре 1, устанавливает конкретную область памяти, тогда другой поток, работающий на ядре 2 может не «увидеть» это изменение, если компилятор не предпринял определенных шагов, чтобы убедиться, что изменение распространяется по ядрам. Кроме того, если вы (программист) не приняли явных мер, чтобы компилятор знал, что определенная переменная будет использоваться несколькими потоками, оптимизатор компилятора может предположить, что эту переменную нельзя изменить вне данный поток выполнения потока, и, следовательно, может полностью удалить ваш тест состояния переменной (потому что, в конце концов, если компилятор «доказал», что значение переменной не может быть изменено, зачем тратить циклы ЦП на проверку ее состояния?).
В вашем случае, скорее всего, происходит то, что вызовы printf()
или usleep()
имеют побочные эффекты (например, переключатель usermode-> kernel-> usermode), которые включают в себя очистку кеша ядра, так что второй поток (при по крайней мере, в конечном итоге) «видит» изменения, сделанные первым потоком - хотя без этих вызовов нет причин синхронизировать кэши, и, следовательно, обновление никогда не «просматривается».
Практический совет: если вы собираетесь совместно использовать переменную в потоках, обязательно либо используйте std::atomic<bool>
вместо простого старого bool
, либо сериализуйте / защитите все обращения (читает и пишет) в эту переменную внутри мьютекса или критической секции. В противном случае вы, скорее всего, столкнетесь с таким «интересным» поведением такого рода.