Хранилища — это операции освобождения, а загрузки — операции приобретения для обоих. я знаю это memory_order_seq_cst
предназначен для наложения дополнительного общего порядка для всех операций, но я не могу построить пример, где это не так, если все memory_order_seq_cst
заменены на memory_order_acq_rel
,
Я что-то пропустил, или разница — это просто эффект документации, то есть нужно использовать memory_order_seq_cst
если вы не хотите играть с более спокойной моделью и использовать memory_order_acq_rel
когда сдерживать расслабленную модель?
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
всегда увеличивается.
Все еще используйте определение и пример из 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 — это точно.