A atribuição de referência é atômica, então por que é interligado.Exchange (objeto ref, objeto) necessário?

StackOverflow https://stackoverflow.com/questions/2192124

Pergunta

No meu serviço Web ASMX multithread, eu tinha um campo de classe _ldata do meu próprio tipo SystemData, que consiste em poucos List<T> e Dictionary<T> marcado como volatile. Os dados do sistema (_allData) é atualizado de vez em quando e eu faço isso criando outro objeto chamado newData e preencha suas estruturas de dados com novos dados. Quando terminar, eu apenas atribui

private static volatile SystemData _allData

public static bool LoadAllSystemData()
{
    SystemData newData = new SystemData();
    /* fill newData with up-to-date data*/
     ...
    _allData = newData.
} 

Isso deve funcionar, pois a atribuição é atômica e os threads que têm a referência aos dados antigos continuam usando e o restante têm os novos dados do sistema logo após a atribuição. No entanto, meu colega disse que em vez de usar volatile Palavra -chave e ASSIGNIMENTO DE PELÓRIO QUE DEVO Usar InterLocked.Exchange Como ele disse que em algumas plataformas não é garantido que a tarefa de referência é atômica. Além disso: quando eu declaro the _allData campo como volatile a

Interlocked.Exchange<SystemData>(ref _allData, newData); 

Produz alerta "uma referência a um campo volátil não será tratada como volátil" O que devo pensar sobre isso?

Foi útil?

Solução

Existem inúmeras perguntas aqui. Considerando -os um de cada vez:

A atribuição de referência é atômica, então por que é interligado.Exchange (objeto ref, objeto) necessário?

A atribuição de referência é atômica. Interligado.Exchange não faz apenas uma atribuição de referência. Ele faz uma leitura do valor atual de uma variável, esconde o valor antigo e atribui o novo valor à variável, tudo como uma operação atômica.

Meu colega disse que, em algumas plataformas, não é garantido que a tarefa de referência é atômica. Meu colega estava correto?

Não. A atribuição de referência é garantida que seja atômica em todas as plataformas .NET.

Meu colega está raciocinando de premissas falsas. Isso significa que suas conclusões estão incorretas?

Não necessariamente. Seu colega pode estar lhe dando bons conselhos por motivos ruins. Talvez haja alguma outra razão pela qual você deva estar usando intertravamento.exchange. A programação livre de trava é incrivelmente difícil e, no momento em que você se afasta de práticas bem estabelecidas adotadas por especialistas no campo, você está nas ervas daninhas e arriscando o pior tipo de condições de corrida. Não sou especialista nesse campo nem um especialista em seu código, por isso não posso fazer um julgamento de uma maneira ou de outra.

Produz alerta "uma referência a um campo volátil não será tratada como volátil" O que devo pensar sobre isso?

Você deve entender por que isso é um problema em geral. Isso levará a uma compreensão de por que o aviso não é importante nesse caso em particular.

A razão pela qual o compilador dá esse aviso é porque a marcação de um campo como volátil significa "esse campo será atualizado em vários tópicos - não gerem nenhum código que cache os valores desse campo e verifique se qualquer leitura ou grava Esse campo não é "movido para frente e para trás no tempo" via inconsistências de cache do processador ".

(Suponho que você já entenda tudo isso. Se você não tem uma compreensão detalhada do significado de volátil e como isso afeta a semântica do cache do processador, não entende como funciona e não deve estar usando programas voláteis. são muito difíceis de acertar; verifique se o seu programa está certo porque você entende como ele funciona, não corretamente por acidente.)

Agora, suponha que você faça uma variável que seja um alias de um campo volátil, passando um ref para esse campo. Dentro do método chamado, o compilador não tem motivo para saber que a referência precisa ter semântica volátil! O compilador gerará alegremente código para o método que falha em implementar as regras para campos voláteis, mas a variável é um campo volátil. Isso pode destruir completamente sua lógica sem bloqueio; A suposição é sempre que um campo volátil é sempre Acessado com semântica volátil. Não faz sentido tratá -lo como volátil às vezes e não outras vezes; você tem que sempre Seja consistente, caso contrário, você não pode garantir consistência em outros acessos.

Portanto, o compilador alerta quando você faz isso, porque provavelmente vai atrapalhar completamente sua lógica sem bloqueio cuidadosamente desenvolvida.

Claro, entrelaçado.exchange é Escrito para esperar um campo volátil e fazer a coisa certa. O aviso é, portanto, enganador. Lamento muito isso; O que deveríamos ter feito é implementar algum mecanismo pelo qual um autor de um método como interlock.Exchange possa colocar um atributo no método dizendo "esse método que leva um ref aplica semântica volátil na variável, portanto, suprime o aviso". Talvez em uma versão futura do compilador o façamos.

Outras dicas

Ou seu colega está enganado ou sabe algo que a especificação de idioma C# não.

5.5 Atomicidade de referências variáveis:

"Leia e escreve sobre os seguintes tipos de dados são atômicos: Bool, Char, Byte, Sbyte, Short, Ushort, Uint, Int, Float e Tipos de Referência".

Portanto, você pode escrever para a referência volátil sem risco de obter um valor corrompido.

É claro que você deve ter cuidado com a forma como decide qual tópico deve buscar os novos dados, para minimizar o risco de que mais de um tópico de cada vez faça isso.

Interligado.Exchange <T>

Define uma variável do tipo t especificado como um valor especificado e retorna o valor original, como uma operação atômica.

Ele muda e retorna o valor original, é inútil porque você só deseja alterá -lo e, como Guffa disse, já é atômico.

A menos que um perfilador, conforme comprovado, que seja um gargalo em seu aplicativo, considere bloqueios de semeaddade, é mais fácil entender e provar que seu código está certo.

Iterlocked.Exchange() não é apenas atômico, também cuida da visibilidade da memória:

As seguintes funções de sincronização usam as barreiras apropriadas para garantir a ordem da memória:

Funções que entram ou deixam seções críticas

Funções que sinalizam objetos de sincronização

Espere funções

Funções interligadas

Problemas de sincronização e multiprocessador

Isso significa que, além da atomicidade, garante que:

  • Para o tópico chamando:
    • Nenhuma reordenação das instruções é feita (pelo compilador, pelo tempo de execução ou pelo hardware).
  • Para todos os tópicos:
    • Nenhuma leitura para a memória que acontece antes que esta instrução verá a alteração que esta instrução foi feita.
    • Todas as leituras após esta instrução verão a alteração feita por esta instrução.
    • Tudo grava na memória depois que esta instrução acontecerá após a alteração da instrução atingir a memória principal (descartando essa mudança para a memória principal quando estiver concluída e não deixar o hardware lavá -lo no tempo).
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top