Написано в псевдокоде cppmem:
int main()
{
atomic_int n = -1;
atomic_int n2 = 0;
{{{
{
n2.store(1, mo_relaxed);
if (n.load(mo_relaxed) != -1)
n.store(1, mo_release);
}
|||
{
n.store(0, mo_release);
int expected = 0;
do
{
desired = n2.load(mo_relaxed);
}
while (!n.compare_exchange_strong(&expected, desired, mo_acquire));
}
}}}
assert(n == 1);
}
Словом, две атомные переменные инициализируются как n = -1 и n2 = 0;
Поток 1 сначала записывает 1 в n2, а затем в n, при условии, что n не было (все еще) -1.
Поток 2 сначала записывает 0 в n, затем загружает n2 и назначает n = n2 до тех пор, пока n не изменилось с момента последнего чтения n (или когда n по-прежнему равно 0).
После объединения обоих потоков n должно равняться 1 в каждом возможном выполнении.
Этот код является частью моего проекта с открытым исходным кодом и связан со сбросом реализации streambuf к началу буфера без блокировки, в то время как два потока одновременно читают и записывают в него. Эта конкретная часть связана с «синхронизацией» (или очисткой записанного вывода).
Я разработал это, и оно работает, когда каждая операция последовательно согласована (это было проверено методом грубой силы), но я не могу обернуться вокруг требований к порядку памяти: /.