C++, если один поток записи переключает bool, когда это сделано, безопасно ли читать этот bool в цикле в одном другом потоке?
Я строю очень простую программу в качестве упражнения.
Идея состоит в том, чтобы вычислить общий размер каталога путем рекурсивной итерации по всему его содержимому и суммирования размеров всех файлов, содержащихся в каталоге (и его подкаталогах).
Чтобы показать пользователю, что программа все еще работает, это вычисление выполняется в другом потоке, в то время как основной поток печатает точку .
раз в секунду.
Теперь основной поток, конечно, должен знать, когда он должен прекратить печатать точки и может найти результат. Можно использовать, например, std::atomic<bool> done(false);
и передать это потоку, который выполнит вычисление, которое установит его true
как только это закончено. Но мне интересно, если в этом простом случае (один поток пишет после завершения, один поток периодически читает до нуля), необходимо ли для этого использовать атомарные типы данных. Очевидно, что если в него могут писать несколько потоков, он должен быть защищен. Но в этом случае есть только один поток записи и один поток чтения.
Нужно ли здесь использовать атомарный тип данных или это излишне, и вместо этого можно использовать обычный тип данных?
3 ответа
Да, это необходимо. Правило состоит в том, что если два потока могут потенциально обращаться к одной и той же памяти одновременно, и хотя бы один из потоков является записывающим, то у вас есть гонка данных. Любое выполнение программы с гонкой данных имеет неопределенное поведение.
Соответствующие цитаты из стандарта C++14:
1.10 / 23
Выполнение программы содержит гонку данных, если она содержит два потенциально одновременных конфликтующих действия, по крайней мере одно из которых не является атомарным, и ни одно не происходит раньше другого, за исключением специального случая для обработчиков сигналов, описанных ниже. Любая такая гонка данных приводит к неопределенному поведению.
1.10 / 6
Две оценки выражений конфликтуют, если одна из них изменяет ячейку памяти (1.7), а другая обращается или изменяет ту же ячейку памяти.
Да, это необходимо.
Проблема заключается в том, что разные ядра процессора могут иметь разные представления об "одних и тех же" данных, в частности, данных, которые кэшируются в ЦП. atomic
part гарантирует, что эти кэши будут правильно очищены, чтобы вы могли безопасно делать то, что пытаетесь сделать.
В противном случае вполне возможно, что другой поток никогда не увидит изменения флага в первом потоке.
Да, это необходимо. В противном случае не гарантируется, что изменения в bool
в одном потоке будет наблюдаться в другом потоке. На самом деле, если компилятор видит, что bool
переменная, по-видимому, никогда больше не используется в потоке выполнения, который ее устанавливает, она может полностью оптимизировать код, который устанавливает значение bool
,