Entendendo std :: atomic :: compare_exchange_weak () no C ++ 11
bool compare_exchange_weak (T& expected, T val, ..);
compare_exchange_weak()
é uma das primitivas de troca de comparação fornecidas em C ++ 11. Estáfraco no sentido de que retorna falso, mesmo que o valor do objeto seja igual aexpected
. Isso é devido aofalha espúria em algumas plataformas em que uma sequência de instruções (em vez de uma como no x86) é usada para implementá-la. Nessas plataformas, a troca de contexto, o recarregamento do mesmo endereço (ou linha de cache) por outro encadeamento etc. pode falhar no primitivo. Estáspurious
como não é o valor do objeto (não é igual aexpected
) que falha na operação. Em vez disso, é um tipo de problema de tempo.
Mas o que me intriga é o que é dito na norma C ++ 11 (ISO / IEC 14882),
29.6.5 .. Uma conseqüência de falhas espúrias é que quase todos os usos de comparação e troca fracas ocorrerão em loop.
Por que tem que estar em um loopquase todos os usos ? Isso significa que devemos fazer um loop quando falha devido a falhas espúrias? Se for esse o caso, por que nos incomodamos em usarcompare_exchange_weak()
e escrever o loop nós mesmos? Podemos apenas usarcompare_exchange_strong()
que acho que deveria nos livrar de falhas espúrias para nós. Quais são os casos de uso comuns decompare_exchange_weak()
?
Outra questão relacionada. Em seu livro "C ++ Concurrency In Action", Anthony diz:
//Because compare_exchange_weak() can fail spuriously, it must typically
//be used in a loop:
bool expected=false;
extern atomic<bool> b; // set somewhere else
while(!b.compare_exchange_weak(expected,true) && !expected);
//In this case, you keep looping as long as expected is still false,
//indicating that the compare_exchange_weak() call failed spuriously.
Porque é!expected
existe na condição de loop? Existe para impedir que todos os threads passem fome e não façam progresso por algum tempo?
Edit: (uma última pergunta)
Nas plataformas em que não existe nenhuma instrução CAS de hardware único, as versões fraca e forte são implementadas usando LL / SC (como ARM, PowerPC, etc.). Existe alguma diferença entre os dois loops a seguir? Por que, se houver? (Para mim, eles devem ter desempenho semelhante.)
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_weak(..))
{ .. }
// use LL/SC (or CAS on x86) and ignore/loop on spurious failures
while (!compare_exchange_strong(..))
{ .. }
Eu vim com esta última pergunta que todos vocês mencionam que talvez haja uma diferença de desempenho dentro de um loop. Também é mencionado pelo C ++ 11 Standard (ISO / IEC 14882):
Quando uma comparação e troca está em loop, a versão fraca gera melhor desempenho em algumas plataformas.
Mas, como analisado acima, duas versões em um loop devem fornecer o mesmo desempenho / similar. O que sinto falta?