Adquirir / liberar semântica com 4 threads

Atualmente, estou lendo Concorrência em C ++ em Ação por Anthony Williams. Uma de suas listagens mostra esse código e ele afirma que a afirmação de quez != 0 pode disparar.

#include <atomic>
#include <thread>
#include <assert.h>

std::atomic<bool> x,y;
std::atomic<int> z;

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()
{
    x=false;
    y=false;
    z=0;
    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);
}

Então, os diferentes caminhos de execução, nos quais consigo pensar, são os seguintes:

1)

Thread a (x is now true)
Thread c (fails to increment z)
Thread b (y is now true)
Thread d (increments z) assertion cannot fire

2)

Thread b (y is now true)
Thread d (fails to increment z)
Thread a (x is now true)
Thread c (increments z) assertion cannot fire

3)

Thread a (x is true)
Thread b (y is true)
Thread c (z is incremented) assertion cannot fire
Thread d (z is incremented)

Alguém poderia me explicar como essa afirmação pode disparar?

Ele mostra este pequeno gráfico:

A loja não deveriay também sincronizar com a cargaread_x_then_ye a loja parax sincronizar com a cargaread_y_then_x? Estou muito confusa.

EDITAR:

Obrigado por suas respostas, eu entendo como os átomos funcionam e como usar Adquirir / Liberar. Eu simplesmente não entendo este exemplo específico. Eu estava tentando descobrir se a afirmação dispara, então o que cada thread fazia? E por que a afirmação nunca dispara se usamos consistência sequencial.

A propósito, estou pensando sobre isso é que sethread a (write_x) armazena parax todo o trabalho realizado até agora é sincronizado com qualquer outro segmento que lêx com aquisição de pedidos. Uma vezread_x_then_y vê isso, ele sai do circuito e lêy. Agora, duas coisas podem acontecer. Em uma opção, owrite_y escreveu paray, o que significa que esta versão será sincronizada com a instrução if (load) que significaz é incrementado e a asserção não pode ser acionada. A outra opção é sewrite_y ainda não foi executado, ou seja, a condição se falhar e z não for incrementado. Nesse cenário, apenasx é verdade ey ainda é falso. Depois que o write_y é executado, o read_y_then_x interrompe seu loop, mas ambosx ey são verdadeiras ez é incrementado e a asserção não é acionada. Não consigo pensar em nenhuma ordem de execução ou memória ondez nunca é incrementado. Alguém pode explicar onde meu raciocínio é falho?

Além disso, eu sei que a leitura do loop sempre será anterior à instrução if, porque a aquisição impede essa reordenação.

questionAnswers(2)

yourAnswerToTheQuestion