Pergunta

Suponha que eu tenho um "contra" variável, e há vários segmentos acessar e ajustar o valor de "balcão" usando Encaixado, ou seja:.

int value = Interlocked.Increment(ref counter);

e

int value = Interlocked.Decrement(ref counter);

Posso assumir que, a mudança feita por Interlocked será visível em todos os tópicos?

Se não, o que devo fazer para que os tópicos sincronizar a variável?

EDIT: alguém me sugeriu usar volátil. Mas quando eu definir o "balcão" tão volátil, não há aviso do compilador "com referência ao campo volátil não será tratada como volátil".

Quando li ajuda on-line, ele disse: "Um campo volátil não deve normalmente ser passado usando um ref ou out parâmetro".

Foi útil?

Solução

InterlockedIncrement / Decrement em x86 CPUs (de x86 bloqueio add / dec) estão criando automaticamente barreira de memória que dá visibilidade a todos os tópicos (ou seja, os tópicos podem ver sua atualização como em ordem, como seqüencial consistência de memória). barreira de memória faz com que todos os pendentes cargas de memória / lojas para ser concluída. volatile não está relacionado com esta questão, embora C # e Java (e alguns compiladores C / C ++) fazer cumprir volatile para fazer barreira de memória. Mas, operação interligada já tem barreira de memória, CPU.

Por favor, também dar uma olhada minha outra resposta em stackoverflow.

Note que eu tenho supor que C # 's InterlockedIncrement / Decrement são mapeamento intrínseco para adicionar bloqueio de x86 / DEC.

Outras dicas

Na verdade, eles não são. Se você deseja modificar com segurança counter, então você está fazendo a coisa correta. Mas se você quiser ler counter diretamente você precisa declará-lo como volatile. Caso contrário, o compilador não tem nenhuma razão para acreditar que counter vai mudar porque as operações Interlocked estão em código que ele não pode ver.

garante bloqueadas que apenas 1 thread por vez pode atualizar o valor. Para garantir que outros segmentos podem ler o valor correto (e não um valor em cache) marcá-lo como volátil.

int volátil pública Contador;

Não; um Interlocked-at-Write-Only sozinho faz não garantir que variável lê no código são realmente fresco; um programa que não lê corretamente a partir de um campo bem pode não ser thread-safe , mesmo sob um "modelo de memória forte". Isto aplica-se a qualquer forma de atribuir a um campo compartilhado entre threads.

Aqui está um exemplo de código que nunca terminará devido ao JIT . (Foi modificada a partir barreiras de memória em. NET para ser um programa LINQPad executável atualizado para a questão).

// Run this as a LINQPad program in "Release Mode".
// ~ It will never terminate on .NET 4.5.2 / x64. ~
// The program will terminate in "Debug Mode" and may terminate
// in other CLR runtimes and architecture targets.
class X {
    // Adding {volatile} would 'fix the problem', as it prevents the JIT
    // optimization that results in the non-terminating code.
    public int terminate = 0;
    public int y;

    public void Run() {
        var r = new ManualResetEvent(false);
        var t = new Thread(() => {
            int x = 0;
            r.Set();
            // Using Volatile.Read or otherwise establishing
            // an Acquire Barrier would disable the 'bad' optimization.
            while(terminate == 0){x = x * 2;}
            y = x;
        });

        t.Start();
        r.WaitOne();
        Interlocked.Increment(ref terminate);
        t.Join();
        Console.WriteLine("Done: " + y);
    }
}

void Main()
{
    new X().Run();
}

A explicação de barreiras de memória em .NET :

Desta vez é JIT, e não o hardware. É claro que JIT tem em cache o valor da variável de terminar [no EAX registrar e do] programa está agora preso no laço destacado acima. .

De qualquer usando um lock ou adicionando um Thread.MemoryBarrier dentro do loop while irá corrigir o problema. Ou você ainda pode usar Volatile.Read [ou um campo volatile]. O objetivo da barreira de memória aqui é apenas para otimizações JIT suprimir. Agora que vimos como software e hardware pode reordenar operações de memória , é hora de discutir barreiras de memória ..

Isto é, um adicional de barreira construção é necessária no lado da leitura para evitar problemas com compilação e JIT re-ordenação / otimizações : esta é uma questão diferente do que a coerência de memória!

Adicionando volatile aqui faria evitar a otimização JIT, e, assim, 'resolver o problema', mesmo que tais resultados em um aviso. Este programa também pode ser corrigido através do uso de Volatile.Read ou uma das várias outras operações que causam uma barreira:. Estas barreiras são tanto uma parte do programa de correção CLR / JIT como as cercas de memória hardware subjacente

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top