Чем отличаются memory_order_seq_cst и memory_order_acq_rel?

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

Я что-то пропустил, или разница — это просто эффект документации, то есть нужно использовать memory_order_seq_cst если вы не хотите играть с более спокойной моделью и использовать memory_order_acq_rel когда сдерживать расслабленную модель?

22

Решение

http://en.cppreference.com/w/cpp/atomic/memory_order имеет хороший пример внизу это работает только с memory_order_seq_cst, по существу memory_order_acq_rel обеспечивает порядок чтения и записи относительно атомарной переменной, а memory_order_seq_cst обеспечивает чтение и запись в глобальном порядке. То есть последовательно согласованные операции видны во всех потоках в одном и том же порядке.

Пример сводится к следующему:

bool x= false;
bool y= false;
int z= 0;

a() { x= true; }
b() { y= true; }
c() { while (!x); if (y) z++; }
d() { while (!y); if (x) z++; }

// kick off a, b, c, d, join all threads
assert(z!=0);

Операции на z защищены двумя атомарными переменными, а не одной, поэтому вы не можете использовать семантику acqu-release для принудительного z всегда увеличивается.

25

Другие решения

Все еще используйте определение и пример из memory_order. Но замените memory_order_seq_cst на memory_order_release в магазине и memory_order_acquire при загрузке.

Релиз-Приобретение заказа гарантирует все, что произошло до хранить в одном потоке становится видимым побочным эффектом в потоке, который сделал загрузку. Но в нашем примере ничего не происходит раньше хранить как в нити0, так и в нити1.

x.store(true, std::memory_order_release); // thread0

y.store(true, std::memory_order_release); // thread1

Более того, без memory_order_seq_cst последовательное упорядочение thread2 и thread3 не гарантируется. Вы можете представить, что они становятся:

if (y.load(std::memory_order_acquire)) { ++z; } // thread2, load y first
while (!x.load(std::memory_order_acquire)); // and then, load x

if (x.load(std::memory_order_acquire)) { ++z; } // thread3, load x first
while (!y.load(std::memory_order_acquire)); // and then, load y

Таким образом, если thread2 и thread3 выполняются до thread0 и thread1, это означает, что x и y остаются ложными, таким образом, ++ z никогда не затрагивается, z остается 0 и срабатывает assert.

Однако, если memory_order_seq_cst входит в изображение, он устанавливает единый общий порядок изменения всех атомарных операций, которые так помечены. Таким образом, в thread2 x.load затем y.load; в thread3 y.load, а затем x.load — это точно.

1

По вопросам рекламы [email protected]