Почему «приобретение / выпуск» не может гарантировать последовательную согласованность в c ++ 11? - PullRequest
0 голосов
/ 21 мая 2018
-Thread 1-                
y.store (20, memory_order_release); 
x.store (10, memory_order_release);

-Thread 2-
if (x.load(memory_order_acquire) == 10) {
  assert (y.load(memory_order_acquire) == 20);
  y.store (10, memory_order_release)
}

-Thread 3-
if (y.load(memory_order_acquire) == 10) {
  assert (x.load(memory_order_acquire) == 10);
}

GCC Atomic Wiki параграф «Общее резюме» гласит: , приведенный выше код assert(x.load(memory_order_acquire)) может потерпеть неудачу .Но я не понимаю, почему?

Насколько я понимаю:

  1. Thread3 не может LoadLoad изменить порядок из-за получить барьер.
  2. Thread1 не может StoreStore изменить порядок из-за барьера release .
  3. Когда Thread2 читает (x) -> 10, x должен быть сброшен из буферного хранилища вкэша в Thread1, поэтому каждый поток знает, что значение x изменилось, например, недействительная строка кэша.
  4. Thread3 использует барьер Acquire , поэтому он может видеть x (10).

1 Ответ

0 голосов
/ 21 мая 2018

Это плохой пример, хотя он иллюстрирует, насколько может быть изнурительной расслабленной атомикой, я полагаю.

[intro.execution] p9:

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

[atomics.order] p2:

Атомная операция A , которая выполняет операцию освобождения для атомарного объекта M , синхронизируется с атомарной операцией B , которая выполняет операцию получения для M и берет свое значение от любого побочного эффекта в последовательности выпуска, озаглавленной A .

В результате показанные оценки объединяются в последовательность переди синхронизированы с отношениями:

Thread 1                   Thread 2              Thread 3

y.store(20)
   |
   | s.b.
   V           s.w.
x.store(10)  -------->  x.load() == 10
                               |
                               | s.b.
                               V      s.w.
                        y.store(10) --------> y.load() == 10
                                                  |
                                                  | s.b.
                                                  V
                                              x.load() == ?

И поэтому каждая оценка в цепочке происходит до следующего (см. [intro.races] p9-10).

[intro.races] стр. 15,

Если вычисление значения A атомарного объекта M происходит до вычисления значения B из M и A берет свое значение из побочного эффекта X при M , тогда значение, вычисленное как B , должно быть либо значением, сохраненным в X или значение, сохраненное побочным эффектом Y на M , где Y следует X в порядке изменения M .

Здесь A - нагрузка в потоке 2, принявшая значение 10, B - нагрузка в потоке 3 (вутверждать).Поскольку A происходит раньше, чем B , и никаких других побочных эффектов для x, B также должно быть 10.


У Херба Саттера гораздо более простой пример в его блоге :

T1: x = 1;
T2: y = 1;
T3: if( x == 1 && y == 0 ) puts("x first");
T4: if( y == 1 && x == 0 ) puts("y first");

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

...