Domanda

Supponiamo di avere una variabile "contatore", e ci sono diversi thread che accedono e impostare il valore di "contatore" utilizzando Interbloccate, cioè:

int value = Interlocked.Increment(ref counter);

e

int value = Interlocked.Decrement(ref counter);

Posso supporre che, la modifica apportata dal Interbloccate sarà visibile in tutti i thread?

Se no, cosa devo fare per rendere tutti i thread di sincronizzare la variabile?

EDIT:qualcuno mi ha suggerito di usare volatili.Ma quando ho impostato il "contatore" come volatile, c'è avviso del compilatore "di riferimento per volatili campo non saranno trattati come volatile".

Quando ho letto la guida in linea, ha detto, "Un campo volatile di norma non devono essere trasmesse utilizzando un ref o fuori parametro".

È stato utile?

Soluzione

InterlockedIncrement/Decremento di Cpu x86, x86 (blocco aggiungere/dec) vengono automaticamente la creazione di barriera di memoria che dà visibilità a tutti i thread (cioè, tutti i thread può vedere il suo aggiornamento in ordine sequenziale coerenza di memoria).Barriera di memoria rende tutti in attesa di memoria carichi di negozi per essere completato. volatile non è legato a questa domanda, anche se il C# e Java (e alcuni compilatori C/C++) applicare volatile per fare barriera di memoria.Ma, interbloccata operazione ha già barriera di memoria da parte della CPU.

Si prega inoltre di dare un'occhiata la mia altra risposta in stackoverflow.

Nota che ho supporre che C#'s InterlockedIncrement/Decremento sono intrinseche mappatura per x86 blocco aggiungere/dic.

Altri suggerimenti

  

Posso presumo che la modifica apportata da Interlocked sarà visibile in tutte le discussioni?

Questo dipende da come si legge il valore. Se si "solo" leggerlo, allora no, questo non sarà sempre visibile in altri thread a meno che non si contrassegna come volatile. Che provoca un avvertimento fastidioso però.

Come alternativa (e molto preferito IMO), leggere usando un'altra istruzione interbloccata. Questo sarà sempre vedere il valore aggiornato su tutte le discussioni:

int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);

che restituisce il valore di lettura, e se fosse 0 swap IT con 0.

La motivazione: l'avviso suggerisce che qualcosa non è giusto; combinando le due tecniche (volatile e interbloccata) non era il modo previsto per farlo.

Aggiornamento: sembra che un altro approccio alla affidabile a 32 bit legge senza l'utilizzo di "volatile" è quello di utilizzare Thread.VolatileRead come suggerito in questa risposta . Ci sono anche alcune prove che io sono completamente sbagliato sull'utilizzo Interlocked per 32 bit si legge, ad esempio questo collegamento problema , anche se mi chiedo se la distinzione è un po 'saccente in natura.

Quello che voglio dire è: non utilizzare questa risposta come la vostra unica fonte; Sto avendo i miei dubbi su questo.

In realtà, non lo sono. Se si desidera modificare counter in sicurezza, allora si sta facendo la cosa giusta. Ma se si desidera leggere volatile direttamente è necessario dichiararla come Interlocked. In caso contrario, il compilatore non ha motivo di credere che <=> cambierà perché le <=> operazioni sono in codice che potrebbe non vedere.

interbloccata assicura che solo 1 thread alla volta può aggiornare il valore. Per garantire che gli altri thread possono leggere il valore corretto (e non un valore memorizzato nella cache) contrassegnare come volatile.

public int contatore volatili;

No; un interbloccata-at-Write-Only solo non assicurare che variabile legge nel codice sono in realtà fresca; di un programma che non legge correttamente da un campo così potrebbe non essere thread-safe , anche sotto un "modello di memoria forte". Questo vale per qualsiasi forma di assegnare a un campo condiviso tra i thread.

Ecco un esempio di codice che non terminerà mai a causa del JIT . (E 'stato modificato da Barriere di memoria in. NET per essere un programma eseguibile LINQPad aggiornato per la domanda).

// 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();
}

La spiegazione da Barriere di memoria in NET :

  

Questa volta è JIT, non l'hardware. E 'chiaro che JIT ha memorizzato il valore della variabile sospendere [nel registro EAX e l'] del programma in corso bloccato nel ciclo evidenziato sopra. .

     

In entrambi utilizzando un lock o l'aggiunta di un Thread.MemoryBarrier all'interno del ciclo while risolverà il problema. Oppure si può anche utilizzare Volatile.Read [o un campo volatile]. Lo scopo della barriera di memoria qui è solo per sopprimere le ottimizzazioni JIT. Ora che abbiamo visto come software e hardware possono riordinare le operazioni di memoria , è il momento di discutere le barriere di memoria ..

Cioè, un ulteriore Barriera costrutto è richiesto sul lato di lettura per evitare problemi con la compilazione e JIT riordino / ottimizzazioni : questo è un problema diverso da quello della coerenza di memoria!

L'aggiunta di <=> qui sarebbe prevenire l'ottimizzazione JIT, e quindi 'risolvere il problema', anche se tali risultati in un messaggio di avviso. Questo programma può anche essere corretto attraverso l'uso di <=> o una delle varie altre operazioni che causano una barriera:. Queste barriere sono tanto una parte del programma correttezza CLR / JIT come le recinzioni memoria hardware sottostanti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top