Die Referenzzuweisung ist atomar. Warum wird also Interlocked.Exchange(ref Object, Object) benötigt?

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

Frage

In meinem Multithread-ASMX-Webdienst hatte ich ein Klassenfeld _allData meines eigenen Typs SystemData, das aus wenigen besteht List<T> Und Dictionary<T> markiert als volatile.Die Systemdaten (_allData) wird von Zeit zu Zeit aktualisiert und ich erstelle dazu ein weiteres Objekt mit dem Namen newData und seine Datenstrukturen mit neuen Daten füllen.Wenn es fertig ist, weise ich es einfach zu

private static volatile SystemData _allData

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

Dies sollte funktionieren, da die Zuweisung atomar ist und die Threads, die auf alte Daten verweisen, diese weiterhin verwenden und der Rest direkt nach der Zuweisung über die neuen Systemdaten verfügt.Allerdings sagte mein Kollege das, anstatt es zu verwenden volatile Schlüsselwort und einfache Aufgabe, die ich verwenden sollte InterLocked.Exchange weil er sagte, dass es auf einigen Plattformen nicht garantiert ist, dass die Referenzzuweisung atomar ist.Darüber hinaus:wenn ich es erkläre the _allData Feld als volatile Die

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

erzeugt die Warnung „Ein Verweis auf ein flüchtiges Feld wird nicht als flüchtig behandelt.“ Was soll ich darüber denken?

War es hilfreich?

Lösung

Hier gibt es zahlreiche Fragen.Betrachten wir sie einzeln:

Die Referenzzuweisung ist atomar. Warum wird also Interlocked.Exchange(ref Object, Object) benötigt?

Die Referenzzuweisung ist atomar.Interlocked.Exchange führt nicht nur eine Referenzzuweisung durch.Es liest den aktuellen Wert einer Variablen, speichert den alten Wert und weist der Variablen den neuen Wert zu, alles als atomare Operation.

Mein Kollege sagte, dass es auf einigen Plattformen nicht garantiert sei, dass die Referenzzuweisung atomar sei.Hatte mein Kollege Recht?

NEIN.Die Referenzzuweisung ist auf allen .NET-Plattformen garantiert atomar.

Mein Kollege geht von falschen Prämissen aus.Bedeutet das, dass ihre Schlussfolgerungen falsch sind?

Nicht unbedingt.Ihr Kollege könnte Ihnen aus schlechten Gründen gute Ratschläge geben.Vielleicht gibt es einen anderen Grund, warum Sie Interlocked.Exchange verwenden sollten.Eine sperrenfreie Programmierung ist wahnsinnig schwierig, und sobald man sich von den etablierten, von Experten auf diesem Gebiet propagierten Praktiken verabschiedet, ist man im Unkraut und riskiert die schlimmsten Rennbedingungen.Ich bin weder ein Experte auf diesem Gebiet noch ein Experte für Ihren Code, daher kann ich kein Urteil in die eine oder andere Richtung fällen.

erzeugt die Warnung „Ein Verweis auf ein flüchtiges Feld wird nicht als flüchtig behandelt.“ Was soll ich darüber denken?

Sie sollten verstehen, warum dies generell ein Problem darstellt.Dies wird zu einem Verständnis dafür führen, warum die Warnung in diesem speziellen Fall unwichtig ist.

Der Grund dafür, dass der Compiler diese Warnung ausgibt, liegt darin, dass das Markieren eines Felds als flüchtig bedeutet: „Dieses Feld wird in mehreren Threads aktualisiert. Generieren Sie keinen Code, der Werte dieses Felds zwischenspeichert, und stellen Sie sicher, dass alle Lese- oder Schreibvorgänge ausgeführt werden.“ Dieses Feld wird nicht aufgrund von Prozessor-Cache-Inkonsistenzen „zeitlich vorwärts und rückwärts verschoben“.

(Ich gehe davon aus, dass Sie das alles bereits verstanden haben.Wenn Sie die Bedeutung von „volatile“ und die Auswirkungen auf die Prozessor-Cache-Semantik nicht im Detail verstehen, verstehen Sie nicht, wie es funktioniert, und sollten „volatile“ nicht verwenden.Es ist sehr schwierig, Programme ohne Sperren richtig zu machen;Stellen Sie sicher, dass Ihr Programm richtig ist, weil Sie verstehen, wie es funktioniert, und nicht durch Zufall.)

Angenommen, Sie erstellen eine Variable, die ein Alias ​​eines flüchtigen Felds ist, indem Sie eine Referenz an dieses Feld übergeben.Innerhalb der aufgerufenen Methode hat der Compiler überhaupt keinen Grund zu wissen, dass die Referenz eine flüchtige Semantik haben muss!Der Compiler generiert fröhlich Code für die Methode, der die Regeln für flüchtige Felder nicht implementiert, aber die Variable Ist ein volatiles Feld.Das kann Ihre sperrenfreie Logik völlig zerstören;Die Annahme ist immer, dass es sich um ein volatiles Feld handelt stets Zugriff mit flüchtiger Semantik.Es macht keinen Sinn, es manchmal als volatil zu behandeln und manchmal nicht;du musst stets Seien Sie konsistent, sonst können Sie die Konsistenz bei anderen Zugriffen nicht garantieren.

Daher warnt der Compiler Sie, wenn Sie dies tun, da dies wahrscheinlich Ihre sorgfältig entwickelte sperrenfreie Logik völlig durcheinander bringen wird.

Natürlich Interlocked.Exchange Ist geschrieben, um ein volatiles Feld zu erwarten und das Richtige zu tun.Die Warnung ist daher irreführend.Ich bereue das sehr;Was wir hätten tun sollen, ist einen Mechanismus zu implementieren, mit dem ein Autor einer Methode wie Interlocked.Exchange der Methode ein Attribut hinzufügen könnte, das besagt: „Diese Methode, die einen Verweis annimmt, erzwingt flüchtige Semantik für die Variable, also unterdrücken Sie die Warnung.“Vielleicht werden wir dies in einer zukünftigen Version des Compilers tun.

Andere Tipps

Entweder ist Ihr Kollegen falsch oder er weiß etwas, das die C# -sprachspezifikation nicht tut.

5.5 Atomizität variabler Referenzen:

"Lese- und Schreibvorgänge der folgenden Datentypen sind atomic: bool, char, byte, sbyte, kurz, uint, int, float und Referenztypen."

Sie können also in die volatile Referenz schreiben, ohne das Risiko eines beschädigten Werts zu erhalten.

Sie sollten natürlich vorsichtig sein, wie Sie entscheiden, welcher Thread die neuen Daten holen soll, um das Risiko zu minimieren, dass mehr als ein Thread gleichzeitig dies tut.

Interlocked.exchange <t>

Legt eine Variable des angegebenen Typs T auf einen angegebenen Wert fest und gibt den ursprünglichen Wert als Atomoperation zurück.

Es ändert sich und gibt den ursprünglichen Wert zurück, es ist nutzlos, weil Sie ihn nur ändern möchten, und, wie Guffa sagte, ist es bereits atomic.

Wenn ein Profiler nicht nachgewiesen wird, dass es sich um einen Engpass in Ihrer Bewerbung handelt, sollten Sie in Betracht ziehen, Schlösser zu entfernen. Es ist einfacher zu verstehen und zu beweisen, dass Ihr Code richtig ist.

Iterlocked.Exchange() ist nicht nur atomic, es kümmert sich auch um die Sichtbarkeit des Gedächtnisses:

Die folgenden Synchronisationsfunktionen verwenden die entsprechenden Hindernisse, um die Speicherbestellung sicherzustellen:

Funktionen, die kritische Abschnitte eingeben oder hinterlassen

Funktionen, die Synchronisationsobjekte signalisieren

Warten Sie Funktionen

Verriegelte Funktionen

Synchronisation und Multiprozessorprobleme

Dies bedeutet, dass sie zusätzlich zur Atomizität sicherstellt:

  • Für den Thread, der es nennt:
    • Es erfolgt keine Neuordnung der Anweisungen (vom Compiler, der Laufzeit oder der Hardware).
  • Für alle Themen:
    • Keine Lesevorgänge in das Speicher, die vor dieser Anweisung geschehen, wird die Änderung dieser Anweisung angezeigt.
    • Alle Lesungen nach dieser Anweisung werden die Änderung dieser Anweisung angezeigt.
    • Alle Schreibvorgänge in das Gedächtnis, nachdem diese Anweisung nach dieser Befehlsänderung stattfinden wird, hat das Hauptspeicher erreicht (durch Spülen dieser Befehl Änderung in das Hauptspeicher, wenn sie fertig ist, und die Hardware nicht bündig zu lassen, wenn sie das Timing besitzt).
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top