Я читал несколько похожих вопросов, но описанные там ситуации немного сложнее.
у меня есть bool b
инициализируется как false
в кучу и две темы. Я понимаю, что операции с bools
являются not atomic
, но, пожалуйста, прочитайте вопрос до конца.
Первый поток может установить b = true
только один раз и больше ничего с этим не делает.
Проверка второго потока b
в цикле, и если это true
делает некоторые действия.
Нужно ли использовать какой-то механизм синхронизации (например, мьютексы) для защиты b
?
Что может случиться, если я этого не сделаю? С ints
Очевидно, что я могу получить произвольные значения, когда читаю и пишу одновременно. Но с bools
есть только true
а также false
и я не против когда-то получить false
вместо true
, Это потенциал SIGSEGV
?
Гонки данных приводят к неопределенному поведению. Что касается стандарта, то соответствующая реализация допускается к segfault.
На практике главная опасность состоит в том, что без синхронизации компилятор будет наблюдать достаточно кода в цикле чтения, чтобы судить, что b
«никогда не меняется» и оптимизировать все, кроме первого чтения значения. Он может сделать это, потому что, если он наблюдает, что в цикле нет синхронизации, то он знает, что любая запись в значение будет гонкой данных. Оптимизатору разрешается предполагать, что ваша программа не вызывает неопределенного поведения, поэтому разрешается предполагать, что нет записей из других потоков.
маркировка b
как volatile
предотвратит эту конкретную оптимизацию на практике, но даже на volatile
Гонки данных объектов имеют неопределенное поведение. Обращение к коду, который оптимизатор «не видит», также на практике предотвратит оптимизацию, поскольку он не знает, изменяет ли этот код b
, Конечно, с оптимизацией по времени соединения / всей программе оптимизатор не может видеть меньше, чем с оптимизацией только во время компиляции.
В любом случае, предотвращение оптимизации программного обеспечения не препятствует тому, чтобы подобное происходило на оборудовании в системе с некогерентными кешами (по крайней мере, поэтому я утверждаю: другие люди утверждают, что это не правильно, и что volatile
Доступ требуется для чтения / записи через кэши. Некоторые реализации ведут себя именно так). Если вы спрашиваете о том, что говорит стандарт, тогда на самом деле не имеет значения, показывает ли аппаратное обеспечение устаревший кэш неопределенно долго, поскольку поведение остается неопределенным, и поэтому реализация может нарушить ваш код независимо от того, конкретный Оптимизация — это то, что ее ломает.
Проблема у вас может быть get заключается в том, что мы не знаем, сколько времени понадобится потоку чтения, чтобы увидеть измененное значение. Если они находятся на разных процессорах с отдельными кешами, никаких гарантий нет, если вы не используете барьер памяти для синхронизации кешей.
В x86 это выполняется автоматически с помощью аппаратного протокола, но не в некоторых других системах.
Нужно ли использовать какой-то механизм синхронизации (например, мьютексы) для защиты
b
?
Если вы этого не сделаете, у вас есть гонка данных. Программы с гонками данных имеют неопределенное поведение. Ответ на этот вопрос совпадает с ответом на вопрос «Хотите ли вы, чтобы ваша программа имела четко определенное поведение?»
Что может случиться, если я этого не сделаю?
Теоретически, все может случиться. Вот что значит неопределенное поведение. Наиболее вероятная плохая вещь, которая может случиться, состоит в том, что «вторая нить» может никогда не увидеть true
значение.
Компилятор может предположить, что у программы нет гонок данных (если она имеет поведение, не определенное стандартом, то поведение, как если бы оно не было нормальным). Поскольку второй поток только когда-либо читает из переменной, которая имеет значение false
и нет синхронизации, которая влияет на эти чтения, логический вывод состоит в том, что значение никогда не меняется, и, следовательно, цикл бесконечен. (и некоторые бесконечные циклы имеют неопределенное поведение в C ++ 11!)
Вот несколько альтернативных решений:
Используйте Mutex, детали были рассмотрены в других ответах выше.
Подумайте об использовании блокировки чтения / записи, которая будет управлять / защищать одновременное чтение и запись. Библиотека pthread предоставляет реализацию: pthread_rwlock_t
В зависимости от того, что делает ваше приложение, рассмотрите возможность использования условной переменной (pthread lib impl: pthread_cond_t). Фактически это сигнал от одного потока к другому, который может позволить вам удалить цикл while и проверку bool.
Достаточно сделать логическое значение volatile (для архитектуры x86) без мьютекса:
volatile bool b;