Instruções atômicas e visibilidade Atualização Variável
-
06-07-2019 - |
Pergunta
Na maioria das plataformas comuns (sendo mais importante o x86, eu entendo que algumas plataformas têm modelos de memória extremamente difíceis que fornecem quase não garante útil para multithreading, mas eu não me importo com contra-exemplos raros), é o seguinte código seguro?
Thread 1:
someVariable = doStuff();
atomicSet(stuffDoneFlag, 1);
Thread 2:
while(!atomicRead(stuffDoneFlag)) {} // Wait for stuffDoneFlag to be set.
doMoreStuff(someVariable);
padrão Assumindo, implementações razoáveis ??de ops atômicas:
- É atribuição da linha 1 do para
someVariable
garantido para concluir antesatomicSet()
é chamado? - É Thread 2 garantido para ver a atribuição para
someVariable
antes de chamardoMoreStuff()
desde que lêstuffDoneFlag
atomicamente?
Edições:
- A implementação de ops atômicas que estou usando contém as instruções x86
LOCK
em cada operação, se isso ajuda. - Suponha
stuffDoneFlag
está devidamente limpo de alguma forma. Como não é importante. - Este é um exemplo muito simplificado. Eu criei-lo desta forma, para que você não teria que entender todo o contexto do problema para respondê-la. Eu sei que não é eficiente.
Solução
Se o seu código x86 real tem a loja para someVariable antes que a loja em atomicSet em Thread 1 e carga de someVariable após a carga em atomicRead em Thread 2, então você deve ser fino. Manual de Software da Intel do desenvolvedor 3A Volume especifica o modelo de memória para x86 na Seção 8.2, e os intra-thread store-de armazenamento e carregamento de carga restrições deve ser suficiente aqui.
No entanto, pode não haver nada impedindo o seu compilador de reordenar as instruções geradas a partir de qualquer linguagem de alto nível que você está usando através das operações atômicas.
Outras dicas
1) Sim
2) Sim
Tanto o trabalho.
Este código é thread-safe, mas eu questiono a eficiência de seu spinlock (o tempo circular) a menos que você está apenas girando por um período muito curto de tempo. Não há nenhuma garantia em qualquer sistema que Thread 2 não vai monopolizar completamente todos os tempos de processamento.
Eu recomendaria usar algumas primitivas de sincronização reais (parece boost :: condition_variable é o que você quer aqui) em vez de contar com o bloqueio de rotação.
As instruções atômicas garantir que os fios 2 aguarda o thread 1 para completar a definição da variável antes de rosca 2 prossegue. Há, no entanto, duas questões-chave:
1) a someVariable
deve ser declarada ' volátil' para garantir que o compilador não otimizar a sua alocação por exemplo, armazená-lo num registo ou adiamento de uma escrita.
2) da segunda rosca está a bloquear, enquanto espera para o sinal (denominado spinlocking ). Sua plataforma provavelmente fornece muito melhor bloqueio e sinalização primatives e mecanismos, mas uma melhoria relativamente simples seria a de simplesmente sleep()
no corpo while()
o fio 2 do.
dsimcha escrito: "Suponha stuffDoneFlag está devidamente limpo de alguma forma Como não é importante.". Isso não é verdade!
Vamos cenário ver:
- cheques Thread2 o stuffDoneFlag se é um começo lendo someVariable.
- Antes do acabamento Thread2 lendo o agendador de tarefas de interrupção da sua missão e suspender a tarefa por algum tempo.
- Thread1 novamente acesso ao someVariable e mudar o conteúdo da memória.
- interruptor Task Scheduler novamente Thread2 e continuar o trabalho, mas o conteúdo da memória de someVariable é alterado!