Frage

Angenommen, ich habe eine Variable „counter“, und es gibt mehrere Threads zugreifen und den Wert des „Zählers“ einstellen mit Verriegelte, d.h.

int value = Interlocked.Increment(ref counter);

und

int value = Interlocked.Decrement(ref counter);

Kann ich davon ausgehen, dass die Änderung von Verschlungene in allen Threads sichtbar sein wird?

Wenn nicht, was soll ich tun, um alle Threads synchronisieren die Variable?

EDIT: schlug jemand vor mir flüchtig zu verwenden. Aber wenn ich die „counter“ als flüchtiges gesetzt, gibt es Compiler-Warnung „Bezug auf flüchtiges Feld nicht als flüchtig behandelt werden“.

Wenn ich die Online-Hilfe zu lesen, sagte er, „Ein flüchtiges Feld sollte in der Regel nicht eine ref übergeben werden mit oder out-Parameter“.

War es hilfreich?

Lösung

InterlockedIncrement / Decrement auf x86-CPUs (x86 des Lock In / dec) schaffen automatisch Speicherbarriere , die Sichtbarkeit für alle Threads gibt (dh alle Fäden seine Aktualisierungs als in Ordnung sehen, wie sequenziellen Speicherkonsistenz). Speicherbarriere macht alle anstehenden Speicherlasten / stores abgeschlossen werden. volatile ist nicht auf diese Frage im Zusammenhang obwohl C # und Java (und einige C / C ++ Compiler) volatile erzwingen zu Speicher Barriere zu machen. Aber verzahnt Betrieb hat bereits Speicherbarriere durch die CPU.

Bitte beachten Sie auch ein Blick meine andere Antwort in Stackoverflow.

Beachten Sie, dass ich davon ausgehen müssen, dass C # 's InterlockedIncrement / Decrement sind intrinsische Mapping auf x86 des lock / dec.

Andere Tipps

  

Kann ich davon ausgehen, dass die Änderung von Verschlungene in allen Threads sichtbar sein wird?

Das hängt davon ab, wie Sie den Wert lesen. Wenn Sie „nur“ sie lesen, dann nein, das wird in anderen Threads nicht immer sichtbar sein, wenn Sie es als flüchtiges markieren. Das verursacht eine lästige Warnung aber.

Als Alternative (und viel bevorzugt IMO), lesen Sie es eine weitere Sperrungsinstruktion verwenden. Dies wird immer den aktualisierten Wert auf allen Threads finden Sie unter:

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

, die den Wert Zu, und wenn es 0 Swaps mit 0.

Motivation: die Warnung deutet an, dass etwas nicht stimmt; die beiden Techniken (volatile & verriegelt) kombiniert war nicht der richtige Weg, dies zu tun.

Update: Es scheint, dass ein anderer Ansatz zum zuverlässigen 32-Bit liest ohne „flüchtig“ verwendet, ist durch Thread.VolatileRead Verwendung als diese Antwort in vorgeschlagen . Es gibt auch einige Hinweise darauf, dass ich völlig falsch bin über Interlocked für 32-Bit unter Verwendung liest, zum Beispiel dieses Connect Problem , obwohl ich frage mich, ob die Unterscheidung etwas pedantisch in der Natur ist.

Was ich wirklich meine, ist: nicht diese Antwort als einzige Quelle verwenden; Ich habe meine Zweifel.

Eigentlich sind sie nicht. Wenn Sie möchten, counter sicher ändern, dann tun Sie das Richtige. Aber wenn Sie counter lesen möchten direkt müssen Sie es als volatile erklären. Ansonsten hat der Compiler keinen Grund zu glauben, dass counter ändern wird, weil die Interlocked Operationen im Code sind, dass sie nicht sehen können.

Verschlungene stellt sicher, dass nur ein Thread zu einem Zeitpunkt, den Wert zu aktualisieren. Um sicherzustellen, dass andere Threads den richtigen Wert lesen (und nicht im Cache gespeicherten Wert) markieren Sie ihn als flüchtig.

public volatile int Zähler;

Nein; ein Verriegelt-at-Write-Only allein macht nicht sicherstellen, dass Variable in Code liest tatsächlich frisch; ein Programm, das nicht korrekt aus einem Feld gelesen hat und vielleicht nicht Thread-sicher , auch unter einem „starken Speichermodell“ sein. Dies gilt für jede Form auf ein Feld der Zuordnung zwischen Threads gemeinsam genutzt.

Hier ist ein Beispiel-Code, der nie enden wird aufgrund der JIT . (Es wurde geändert von Speicherbarrieren in. NET ein lauffähiges LINQPad Programm für die Frage aktualisiert werden).

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

Die Erklärung von Speicherbarrieren in .NET :

  

Dieses Mal ist es JIT ist, nicht die Hardware. Es ist klar, dass JIT den Wert der Variablen beenden zwischengespeichert hat [im EAX-Register und das] Programm ist nun fest in der Schleife oben hervorgehoben. .

     

Entweder mit einem lock oder eine Thread.MemoryBarrier innerhalb der while-Schleife wird das Hinzufügen das Problem beheben. Oder Sie können sogar Volatile.Read [oder ein volatile Feld] verwenden. Der Zweck der Speicherbarriere hier ist nur JIT-Optimierungen zu unterdrücken. Jetzt, da wir gesehen haben, wie Software- und Hardware-Speicheroperationen neu anordnen können , es ist Zeit, Speicher Barrieren zu diskutieren ..

Das heißt, eine zusätzlicher Barriere Konstrukt auf der Leseseite erforderlich ist, um verhindert Probleme mit Kompilierung und JIT Nachbestellung / Optimierungen : das ist ein anderes Thema als Speicher-Kohärenz!

volatile Hinzufügen von hier würde verhindert die JIT-Optimierung und damit 'das Problem beheben', auch wenn diese Ergebnisse in einer Warnung. Dieses Programm kann auch durch die Verwendung von Volatile.Read oder einen der verschiedenen anderen Operationen, die eine Barriere korrigiert werden verursachen. Diese Barrieren sind ebenso ein Teil des CLR / JIT-Programm Korrektheit als die zugrunde liegenden Hardware-Speicher Zäunen

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top