Подтвердить что ты не робот

Будут ли две непринужденные записи в одно и то же место в разных потоках, всегда будут видны в том же порядке другими потоками?

В архитектуре x86 магазины в том же месте памяти имеют общий порядок, например, см. это видео. Каковы гарантии в модели памяти С++ 11?

Точнее, в

-- Initially --
std::atomic<int> x{0};

-- Thread 1 --
x.store(1, std::memory_order_release);

-- Thread 2 --
x.store(2, std::memory_order_release);

-- Thread 3 --
int r1 = x.load(std::memory_order_acquire);
int r2 = x.load(std::memory_order_acquire);

-- Thread 4 --
int r3 = x.load(std::memory_order_acquire);
int r4 = x.load(std::memory_order_acquire);

разрешен ли исход r1==1, r2==2, r3==2, r4==1 (по какой-либо архитектуре, отличной от x86)? Что делать, если я должен заменить все memory_order на std::memory_order_relaxed?

4b9b3361

Ответ 1

Нет, такой результат не допускается. §1.10 [intro.multithread]/p8, 18 (цитирование N3936/С++ 14, тот же текст содержится в параграфах 6 и 16 для N3337/С++ 11):

8 Все модификации конкретного атомного объекта M происходят в некоторых конкретный общий порядок, называемый порядком модификации М.

18 Если вычисление значения А атомного объекта М происходит до вычисление значения B из M, а A берет свое значение от побочного эффекта X на M, тогда значение, вычисленное B, должно быть либо значением, X или значение, сохраненное побочным эффектом Y на M, где Y следует за X в порядок модификации M. [Примечание: это требование известно как согласованность чтения и чтения. -end note]

В вашем коде есть два побочных эффекта, а через p8 они встречаются в определенном полном порядке. В Thread 3 вычисление значения для вычисления значения, которое должно быть сохранено в r1, происходит раньше, чем значение r2, поэтому, учитывая r1 == 1 и r2 == 2, мы знаем, что хранилище, выполняемое потоком 1, предшествует хранилищу, выполняемому Thread 2 в порядке модификации x. В этом случае Thread 4 не может наблюдать r3 == 2, r4 == 1, не запуская p18. Это независимо от используемого memory_order.

В p21 (p19 в N3337) есть заметка, которая имеет значение:

[Примечание: четыре предшествующих требования когерентности эффективно запретить переупорядочение компиляторами атомных операций одному объекту, даже если обе операции являются ослабленными нагрузками. Это эффективно делает гарантия когерентности кеш-памяти, предоставляемая большинством аппаратных средств, доступных для С++ атомных операций. -end note]

Ответ 2

Per С++ 11 [intro.multithread]/6: "Все модификации конкретного атомарного объекта M встречаются в определенном полном порядке, называемом порядком модификации M." Следовательно, чтение атомарного объекта определенным потоком никогда не увидит "более старые" значения, чем те, которые уже наблюдал поток. Обратите внимание, что здесь не упоминается порядок памяти, поэтому это свойство верное для всех из них - seq_cst через relaxed.

В примере, приведенном в OP, порядок модификации x может быть либо (0,1,2), либо (0,2,1). Поток, который наблюдал заданное значение в этом порядке модификации, не может впоследствии наблюдать более раннее значение. Результат r1==1, r2==2 означает, что порядок модификации x равен (0,1,2), но r3==2, r4==1 означает, что это (0,2,1), противоречие. Таким образом, результат невозможен для реализации, которая соответствует С++ 11.