В документации std::memory_order
на cppreference.com есть пример непринужденного заказа:
Расслабленный заказ
Маркированные атомарные операции
memory_order_relaxed
не являются операциями синхронизации; они не навязывают порядок между одновременными обращениями к памяти. Они гарантируют только атомарность и согласованность порядка модификации.Например, с х и у изначально ноль,
// Thread 1: r1 = y.load(std::memory_order_relaxed); // A x.store(r1, std::memory_order_relaxed); // B // Thread 2: r2 = x.load(std::memory_order_relaxed); // C y.store(42, std::memory_order_relaxed); // D
разрешено производить r1 == r2 == 42, потому что, хотя A секвенируется перед B в потоке 1, а C секвенируется перед D в потоке 2, ничто не мешает D появляться перед A в порядке изменения y, а B - появляются перед C в порядке модификации x. Побочный эффект D на y может быть виден нагрузке A в потоке 1, в то время как побочный эффект B на x может быть виден нагрузке C в потоке 2. В частности, это может произойти, если D завершен до C в поток 2, либо из-за переупорядочения компилятора, либо во время выполнения.
он говорит: «C секвенируется перед D в потоке 2».
Согласно определению секвенированного до, которое можно найти в Порядке оценки , если A секвенируется до B, тогда оценка A будет завершена до того, как начнется оценка B. Поскольку C секвенируется перед D в потоке 2, C должен быть завершен до начала D, следовательно, условие условия последнего предложения снимка никогда не будет выполнено.
источник
Ответы:
Я считаю, что cppreference это правильно. Я думаю, что это сводится к правилу «как будто» [intro.execution] / 1 . Компиляторы обязаны воспроизводить только наблюдаемое поведение программы, описанное вашим кодом. Секвенировал-прежде , чем отношение только устанавливается между оценками с точки зрения потока , в котором эти оценки выполняются [intro.execution] / 15 . Это означает, что когда две оценки, последовательно расположенные одна за другой, появляются где-то в каком-то потоке, код, фактически выполняющийся в этом потоке, должен вести себя так, как будто то, что делает первая оценка, действительно влияет на то, что делает вторая оценка. Например
must print 42. Однако компилятору на самом деле не нужно сохранять значение 42 в объекте
x
перед тем, как прочитать значение из этого объекта, чтобы напечатать его. Он также может помнить, что последнее значение, которое должно быть сохранено,x
было 42, а затем просто печатать значение 42 непосредственно перед выполнением фактического сохранения значения 42 вx
. Фактически, еслиx
это локальная переменная, она также может просто отслеживать, какое значение этой переменной было в последний раз присвоено в любой точке, и никогда даже не создавать объект или фактически хранить значение 42. Поток не может определить разницу. Поведение всегда будет таким, как если бы существовала переменная, и как если бы значение 42 действительно сохранялось в объектеx
ранеезагружается из этого объекта. Но это не значит, что сгенерированный машинный код должен хранить и загружать что-либо где-либо. Все, что требуется, - это то, что наблюдаемое поведение сгенерированного машинного кода неотличимо от того, что было бы, если бы все эти вещи действительно происходили.Если мы посмотрим на
тогда да, C секвенируется перед D. Но если смотреть из этого потока изолированно, ничто из того, что делает C, не влияет на результат D. И ничто из того, что делает D, не изменяет результат C. Единственный способ, которым один может повлиять на другой, - это как косвенное следствие того, что происходит в другом потоке. Однако, указав
std::memory_order_relaxed
, вы явно заявиличто порядок, в котором загрузка и хранение наблюдаются другим потоком, не имеет значения. Поскольку ни один другой поток не может наблюдать за загрузкой и хранением в каком-либо определенном порядке, другой поток не может ничего сделать, чтобы заставить C и D последовательно влиять друг на друга. Таким образом, порядок, в котором загрузка и хранение фактически выполняются, не имеет значения. Таким образом, компилятор может изменить их порядок. И, как упомянуто в пояснении под этим примером, если сохранение из D выполняется до загрузки из C, тогда r1 == r2 == 42 действительно может произойти ...источник
Иногда возможно упорядочить действие относительно двух других последовательностей действий, не предполагая какого-либо относительного упорядочения действий в этих последовательностях относительно друг друга.
Предположим, например, что у человека есть три следующих события:
и чтение p2 независимо упорядочивается после записи p1 и перед записью p3, но нет особого порядка, в котором участвуют как p1, так и p3. В зависимости от того, что сделано с p2, компилятору может быть нецелесообразно отложить p1 после p3 и все же достичь требуемой семантики с p2. Предположим, однако, что компилятор знал, что приведенный выше код был частью большой последовательности:
В этом случае он может определить, что он может переупорядочить хранилище в p1 после вышеприведенного кода и объединить его со следующим хранилищем, что приведет к коду, который записывает p3 без записи p1:
Хотя может показаться, что зависимости данных приведут к тому, что определенные части отношений секвенирования будут вести себя транзитивно, компилятор может идентифицировать ситуации, когда очевидных зависимостей данных не существует, и, следовательно, не будет иметь переходных эффектов, которые можно было бы ожидать.
источник
Если есть два оператора, компилятор будет генерировать код в последовательном порядке, поэтому код для первого будет размещен перед вторым. Но внутри процессора есть конвейеры, и они могут выполнять сборочные операции параллельно. Оператор C является инструкцией загрузки. Во время извлечения памяти конвейер будет обрабатывать следующие несколько инструкций, и, учитывая, что они не зависят от инструкции загрузки, они могут в конечном итоге быть выполнены до завершения C (например, данные для D находились в кеше, C в основной памяти).
Если пользователю действительно необходимо выполнить два оператора последовательно, можно использовать более строгие операции упорядочения памяти. В общем, пользователям все равно, если программа логически верна.
источник
Все, что вы думаете, одинаково справедливо. Стандарт не говорит, что выполняется последовательно, что нет, и как это можно перепутать .
Это зависит от вас и от каждого программиста - создать согласованную семантику поверх этого беспорядка - работы, достойной нескольких докторов наук.
источник