Безопасно ли одновременно перезаписывать переменную с тем же значением? - PullRequest
3 голосов
/ 17 августа 2010

У меня следующая ситуация (вызванная дефектом в коде):

Существует общая переменная примитивного типа (пусть это будет int), которая инициализируется при запуске программы из строго одного потока в значение N (пусть это будет 0). Затем (строго после инициализации переменной ) во время выполнения программы запускаются различные потоки, и они в произвольном порядке либо читают эту переменную, либо перезаписывают ее с тем же самым значением N (0 в этом примере). Синхронизация доступа к переменной отсутствует.

Может ли эта ситуация вызвать непредвиденное поведение в программе?

Ответы [ 6 ]

5 голосов
/ 17 августа 2010

Это невероятно маловероятно, но не невозможно в соответствии со стандартом.

Нет ничего, что указывало бы, что является базовым представлением целого числа, разве стандарт не определяет, как загружаются значения.

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

Если другой поток считывает значение во время смещения битовой комбинации (например, 00000001, 00000010, 00000101 и т. Д.), У вас возникнет проблема.

Возможностилюбой, кто проектирует такую ​​причудливую архитектуру, настолько близок к нулю, что может быть незначительным.Но, к сожалению, это не ноль.Все, что я пытаюсь донести, - это то, что вы вообще не должны полагаться на предположения, когда речь идет о соответствии стандартам.

И, пожалуйста, прежде чем голосовать против меня, не стесняйтесь цитировать часть стандартав котором говорится, что это невозможно: -)

4 голосов
/ 17 августа 2010

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

В C ++ 0x (который имеет стандартную модель параллелизма) ваш сценарий формально приведет к неопределенному поведению.В проекте окончательного комитета C ++ 0x §1.10 содержится длинная, подробная и трудная для чтения спецификация модели параллелизма, но в основном она сводится к следующему:

Конфликт двух выраженийесли один из них изменяет ячейку памяти, а другой обращается или изменяет ту же ячейку памяти (§1.10 / 3).

Выполнение программы содержит гонку данных, если она содержит два конфликтующих действия в разных потоках.по крайней мере один из которых не является атомарным, и ни один не происходит раньше другого.Любая такая гонка данных приводит к неопределенному поведению (§1.10 / 14).

Ваши оценки выражений явно конфликтуют, потому что они изменяют и читают одно и то же место в памяти, и поскольку объект не является атомарным, а доступ не являетсясинхронизируется с помощью блокировки, у вас неопределенное поведение.

2 голосов
/ 17 августа 2010

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

1 голос
/ 17 августа 2010

Да, в этом случае возможно непредвиденное поведение.Рассмотрим случай, когда начальное значение переменной не было 0. Один поток может начать установку в 0, а другой поток увидит переменную только с некоторыми установленными байтами.* это очень маловероятно, так как большинство процессоров будут иметь атомарное назначение значений размера слова.Однако, как только вы достигнете 8-битных числовых значений (long на некоторых платформах) или больших структур, это станет проблемой.

1 голос
/ 17 августа 2010

Вы не можете действительно положиться на это.С примитивными типами у вас должно быть все в порядке, и если операция атомарная (например, правильно выровненный int на большинстве платформ), тогда запись и чтение различных значений безопасны (обратите внимание, что я имею в виду «x = 5;», не "x + = 5;", который никогда не является атомарным и не является потокобезопасным).

Для не примитивных типов, даже если его одинаковое значение, все ставки отключены, поскольку может существовать конструктор копирования, которыйделает что-то небезопасное (например, выделение памяти).

0 голосов
/ 17 августа 2010

Если никакой другой поток (включая основной поток) не может изменить значение 0 на что-либо другое (скажем, 1), пока эти потоки инициализируются, тогда у вас не возникнет проблем. Но если какой-либо другой поток может изменить значение на этапе запуска, у вас могут возникнуть проблемы. Вы играете в опасную игру, и я бы порекомендовал заблокировать ее перед прочтением значения.

...