Приобретение / выпуск VS последовательной согласованности в C++11?
#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_release);
}
void write_y()
{
y.store(true, std::memory_order_release);
}
void read_x_then_y()
{
while (!x.load(std::memory_order_acquire))
;
if (y.load(std::memory_order_acquire)) {
++z;
}
}
void read_y_then_x()
{
while (!y.load(std::memory_order_acquire))
;
if (x.load(std::memory_order_acquire)) {
++z;
}
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join(); b.join(); c.join(); d.join();
assert(z.load() != 0);
}
Если я переназначу seq_cst для получения / выпуска в последнем примере cppreference, могу assert(z.load() != 0)
потерпеть неудачу?
- Seq_CST может предотвратить переупорядочение StoreLoad, но код этого не сделал.
- Приобретение может предотвратить переупорядочение LoadLoad.
- Выпуск может помешать повторному заказу StoreStore.
2 ответа
Основное свойство, которое не гарантируется приобретением / выпуском, представляет собой единый общий порядок изменений. Это только гарантирует, что (несуществующие) предыдущие действия a
а также b
наблюдаются c
а также d
если они видят true
от нагрузок.
Очевидным примером этого является система с несколькими процессорами (физическими сокетами). Die 1 имеет ядро A бегущей нити a
и ядро С бегущей нитью c
, Умирает 2 имеет ядро B бегущей нити b
и ядро D работает поток d
, Соединение между двумя сокетами имеет большую задержку по сравнению с операцией памяти, которая попадает в кэш-память.
a
а также b
бежать в то же время настенные часы. C находится в состоянии покоя с A, поэтому можно увидеть магазин, чтобы x
немедленно, но соединение задерживает наблюдение за магазином y
Таким образом, он видит старое значение. Точно так же D находится в живых с B, поэтому он видит магазин y
, но скучает по магазину x
,
Принимая во внимание, что если у вас есть последовательная согласованность, требуется некоторая координация для обеспечения полного порядка, например, "C и D блокируются, пока межсоединение синхронизирует кэши".
Да, возможно, что z.load() == 0
в вашем коде, если вы используете acquire
/release
заказать, как вы сделали. Между независимыми записями не происходит никаких отношений x
а также y
, Это не случайное совпадение, использованное в этом примере специально для иллюстрации случая, когда приобретение / выпуск не достаточно.
Это иногда называют IRIW (независимые чтения независимых записей), и в некоторых моделях упорядочения аппаратных средств, как правило, их скрывают. В частности, модель памяти, определяемая только с точки зрения возможной загрузки-загрузки, загрузки-хранения, хранения-хранения и т. Д., Переупорядочений, в действительности ничего не говорит об IRIW. В модели памяти x86 переупорядочение IRIW запрещено из-за предложения, объясняющего, что хранилища имеют общий порядок, и все процессоры просматривают хранилища в этом же порядке.
Я не знаю, допускают ли какие-либо процессоры общего пользования повторный порядок IRIW при использовании барьеров и / или инструкций, необходимых для получения и выпуска, но я не удивлюсь, если некоторые из них это сделают.