Другие люди давали вам ответы, цитируя соответствующие части стандарта, которые утверждают, что гарантия, которую вы считаете существующей, не существует.Похоже, что вы интерпретируете часть стандарта, в которой говорится, что определенное странное поведение разрешено для атомарных объектов, если вы используете memory_order_relaxed
как означающее, что это поведение недопустимо для неатомарных объектов.Это скачок, к которому явно обращаются другие части стандарта, которые объявляют поведение неопределенным для неатомарных объектов.
В практическом смысле, здесь приведен порядок событий, которые могут произойти в потоке 1, которыйбыло бы вполне разумно, но в результате поведение, которое вы считаете запрещенным, даже если аппаратное обеспечение гарантировало, что весь доступ к памяти был полностью сериализован между процессорами.Имейте в виду, что стандарт должен учитывать не только поведение оборудования, но и поведение оптимизаторов, которые часто агрессивно переупорядочивают и переписывают код.
Поток 1 может быть переписанОптимизатор выглядит следующим образом:
old_y = y; // old_y is a hidden variable (perhaps a register) created by the optimizer
y = 42;
if (x != 42) y = old_y;
У оптимизатора могут быть совершенно разумные причины для этого.Например, он может решить, что гораздо вероятнее, чем не записать 42
в y
, и по причинам зависимости конвейер может работать намного лучше, если сохранение в y
происходит раньше, а не позже.
Правило состоит в том, что видимый результат должен выглядеть , как если бы код, который вы написали, был выполнен.Но не требуется, чтобы код, который вы пишете, имел какое-либо сходство с тем, что на самом деле велено делать процессору.
Атомарные переменные накладывают ограничения на способность компилятора переписывать код, а такжепоручение компилятору выдавать специальные инструкции ЦП, которые накладывают ограничения на способность ЦП переупорядочивать доступ к памяти.Ограничения, включающие memory_order_relaxed
, намного сильнее, чем обычно.Компилятору обычно разрешают полностью избавиться от любых ссылок на x
и y
, если они не являются атомарными.
Кроме того, если они атомарные, компилятор должен гарантировать, что другие процессорыувидеть всю переменную как с новым или старым значением.Например, если переменная является 32-разрядным объектом, который пересекает границу строки кэша, а модификация включает в себя изменение битов по обе стороны от границы строки кэша, один ЦП может увидеть значение переменной, которое никогда не записывается, потому что он видит толькообновление битов на одной стороне границы строки кэша.Но это недопустимо для атомарных переменных, измененных с помощью memory_order_relaxed
.
. Именно поэтому гонки данных помечены стандартом как неопределенное поведение.Пространство возможных вещей, которые могут произойти, вероятно, гораздо более дикое, чем может представить ваше воображение, и, конечно, шире, чем разумно охватить любой стандарт.