Опасности одновременной записи и чтения логического значения в простой ситуации

Я читал несколько похожих вопросов, но описанные там ситуации немного сложнее.

у меня есть bool b инициализируется как false в кучу и две темы. Я понимаю, что операции с bools являются not atomic, но, пожалуйста, прочитайте вопрос до конца.

Первый поток может установить b = true только один раз и больше ничего с этим не делает.
Проверка второго потока b в цикле, и если это true делает некоторые действия.

Нужно ли использовать какой-то механизм синхронизации (например, мьютексы) для защиты b?
Что может случиться, если я этого не сделаю? С ints Очевидно, что я могу получить произвольные значения, когда читаю и пишу одновременно. Но с bools есть только true а также false и я не против когда-то получить false вместо true, Это потенциал SIGSEGV?

3

Решение

Гонки данных приводят к неопределенному поведению. Что касается стандарта, то соответствующая реализация допускается к segfault.

На практике главная опасность состоит в том, что без синхронизации компилятор будет наблюдать достаточно кода в цикле чтения, чтобы судить, что b «никогда не меняется» и оптимизировать все, кроме первого чтения значения. Он может сделать это, потому что, если он наблюдает, что в цикле нет синхронизации, то он знает, что любая запись в значение будет гонкой данных. Оптимизатору разрешается предполагать, что ваша программа не вызывает неопределенного поведения, поэтому разрешается предполагать, что нет записей из других потоков.

маркировка b как volatile предотвратит эту конкретную оптимизацию на практике, но даже на volatile Гонки данных объектов имеют неопределенное поведение. Обращение к коду, который оптимизатор «не видит», также на практике предотвратит оптимизацию, поскольку он не знает, изменяет ли этот код b, Конечно, с оптимизацией по времени соединения / всей программе оптимизатор не может видеть меньше, чем с оптимизацией только во время компиляции.

В любом случае, предотвращение оптимизации программного обеспечения не препятствует тому, чтобы подобное происходило на оборудовании в системе с некогерентными кешами (по крайней мере, поэтому я утверждаю: другие люди утверждают, что это не правильно, и что volatile Доступ требуется для чтения / записи через кэши. Некоторые реализации ведут себя именно так). Если вы спрашиваете о том, что говорит стандарт, тогда на самом деле не имеет значения, показывает ли аппаратное обеспечение устаревший кэш неопределенно долго, поскольку поведение остается неопределенным, и поэтому реализация может нарушить ваш код независимо от того, конкретный Оптимизация — это то, что ее ломает.

7

Другие решения

Проблема у вас может быть get заключается в том, что мы не знаем, сколько времени понадобится потоку чтения, чтобы увидеть измененное значение. Если они находятся на разных процессорах с отдельными кешами, никаких гарантий нет, если вы не используете барьер памяти для синхронизации кешей.

В x86 это выполняется автоматически с помощью аппаратного протокола, но не в некоторых других системах.

3

Нужно ли использовать какой-то механизм синхронизации (например, мьютексы) для защиты b?

Если вы этого не сделаете, у вас есть гонка данных. Программы с гонками данных имеют неопределенное поведение. Ответ на этот вопрос совпадает с ответом на вопрос «Хотите ли вы, чтобы ваша программа имела четко определенное поведение?»

Что может случиться, если я этого не сделаю?

Теоретически, все может случиться. Вот что значит неопределенное поведение. Наиболее вероятная плохая вещь, которая может случиться, состоит в том, что «вторая нить» может никогда не увидеть true значение.

Компилятор может предположить, что у программы нет гонок данных (если она имеет поведение, не определенное стандартом, то поведение, как если бы оно не было нормальным). Поскольку второй поток только когда-либо читает из переменной, которая имеет значение falseи нет синхронизации, которая влияет на эти чтения, логический вывод состоит в том, что значение никогда не меняется, и, следовательно, цикл бесконечен. (и некоторые бесконечные циклы имеют неопределенное поведение в C ++ 11!)

2

Вот несколько альтернативных решений:

  1. Используйте Mutex, детали были рассмотрены в других ответах выше.

  2. Подумайте об использовании блокировки чтения / записи, которая будет управлять / защищать одновременное чтение и запись. Библиотека pthread предоставляет реализацию: pthread_rwlock_t

  3. В зависимости от того, что делает ваше приложение, рассмотрите возможность использования условной переменной (pthread lib impl: pthread_cond_t). Фактически это сигнал от одного потока к другому, который может позволить вам удалить цикл while и проверку bool.

0

Достаточно сделать логическое значение volatile (для архитектуры x86) без мьютекса:

volatile bool b;
-2
По вопросам рекламы [email protected]