Volatile vs. Interlocked vs. Lock

670

Angenommen, eine Klasse verfügt über ein public int counterFeld, auf das mehrere Threads zugreifen. Dies intwird nur erhöht oder verringert.

Welcher Ansatz sollte verwendet werden, um dieses Feld zu erhöhen, und warum?

  • lock(this.locker) this.counter++;,
  • Interlocked.Increment(ref this.counter);,
  • Ändern Sie den Zugriffsmodifikator von counterin public volatile.

Jetzt, wo ich entdeckt habe volatile, habe ich viele lockAussagen und die Verwendung von entfernt Interlocked. Aber gibt es einen Grund, dies nicht zu tun?

Ader
quelle
Lesen Sie die Threading in C # -Referenz. Es behandelt die Vor- und Nachteile Ihrer Frage. Jeder der drei hat unterschiedliche Zwecke und Nebenwirkungen.
Spoulson
1
simple-talk.com/blogs/2012/01/24/… Sie können die Verwendung von volitable in Arrays sehen, ich verstehe es nicht vollständig, aber es ist ein weiterer Hinweis darauf, was dies tut.
eran otzap
50
Das ist so, als würde man sagen "Ich habe festgestellt, dass die Sprinkleranlage niemals aktiviert ist, also werde ich sie entfernen und durch Rauchmelder ersetzen." Der Grund, dies nicht zu tun, ist, dass es unglaublich gefährlich ist und Ihnen fast keinen Nutzen bringt . Wenn Sie Zeit haben, den Code zu ändern, finden Sie einen Weg, ihn weniger multithreaded zu machen ! Finden Sie keinen Weg, den Multithread-Code gefährlicher und leichter kaputt zu machen!
Eric Lippert
1
Mein Haus hat sowohl Sprinkler als auch Rauchmelder. Wenn Sie einen Zähler in einem Thread erhöhen und in einem anderen lesen, benötigen Sie anscheinend sowohl eine Sperre (oder eine gesperrte) als auch das flüchtige Schlüsselwort. Wahrheit?
Yooy
2
@yoyo Nein, du brauchst nicht beides.
David Schwartz

Antworten:

857

Das Schlimmste (funktioniert eigentlich nicht)

Ändern Sie den Zugriffsmodifikator von counterinpublic volatile

Wie andere bereits erwähnt haben, ist dies allein überhaupt nicht sicher. Der Punkt volatileist, dass mehrere Threads, die auf mehreren CPUs ausgeführt werden, Daten zwischenspeichern können und werden und Anweisungen neu anordnen.

Wenn dies nicht der volatile Fall ist und CPU A einen Wert erhöht, sieht CPU B diesen inkrementierten Wert möglicherweise erst einige Zeit später, was zu Problemen führen kann.

Wenn volatiledies der Fall ist , wird nur sichergestellt, dass die beiden CPUs gleichzeitig dieselben Daten sehen. Es hindert sie überhaupt nicht daran, ihre Lese- und Schreibvorgänge zu verschachteln, was das Problem ist, das Sie vermeiden möchten.

Zweitbester:

lock(this.locker) this.counter++;;

Dies ist sicher (vorausgesetzt, Sie erinnern sich an alle lockanderen Stellen, auf die Sie zugreifen this.counter). Es verhindert, dass andere Threads anderen Code ausführen, der von geschützt wird locker. Die Verwendung von Sperren verhindert auch die oben beschriebenen Probleme bei der Neuordnung mehrerer CPUs, was großartig ist.

Das Problem ist, dass das Sperren langsam ist. Wenn Sie das lockeran einem anderen Ort wiederverwenden, der nicht wirklich verwandt ist, können Sie Ihre anderen Threads ohne Grund blockieren.

Beste

Interlocked.Increment(ref this.counter);

Dies ist sicher, da das Lesen, Inkrementieren und Schreiben effektiv in einem Treffer ausgeführt wird, der nicht unterbrochen werden kann. Aus diesem Grund wirkt sich dies nicht auf anderen Code aus, und Sie müssen auch nicht daran denken, an anderer Stelle zu sperren. Es ist auch sehr schnell (wie MSDN sagt, ist dies auf modernen CPUs oft buchstäblich eine einzelne CPU-Anweisung).

Ich bin mir jedoch nicht ganz sicher, ob es darum geht, dass andere CPUs Dinge neu anordnen oder ob Sie auch flüchtig mit dem Inkrement kombinieren müssen.

InterlockedNotes:

  1. VERRIEGELTE METHODEN SIND AUF JEDER ANZAHL VON KERNEN ODER CPUS KONZURENT SICHER.
  2. Interlocked-Methoden wenden einen vollständigen Zaun um die von ihnen ausgeführten Anweisungen an, sodass keine Neuordnung erfolgt.
  3. Interlocked-Methoden benötigen oder unterstützen den Zugriff auf ein flüchtiges Feld nicht , da Volatile einen halben Zaun um Operationen auf einem bestimmten Feld legt und Interlocked den vollständigen Zaun verwendet.

Fußnote: Wofür flüchtig ist eigentlich gut.

Wie volatilenicht diese Art von Multithreading - Probleme zu verhindern, was ist es? Ein gutes Beispiel ist, dass Sie zwei Threads haben, einen, der immer in eine Variable schreibt (z. B. queueLength), und einen, der immer aus derselben Variablen liest.

Wenn queueLengthes nicht flüchtig ist, kann Thread A fünfmal schreiben, aber Thread B kann diese Schreibvorgänge als verzögert (oder möglicherweise sogar in der falschen Reihenfolge) ansehen.

Eine Lösung wäre das Sperren, aber Sie könnten in dieser Situation auch volatile verwenden. Dies würde sicherstellen, dass Thread B immer das aktuellste sieht, was Thread A geschrieben hat. Beachten Sie jedoch, dass diese Logik nur funktioniert, wenn Sie Autoren haben, die nie lesen, und Leser, die nie schreiben, und wenn das, was Sie schreiben, ein atomarer Wert ist. Sobald Sie ein einzelnes Lese-, Änderungs- und Schreibvorgang ausführen, müssen Sie zu Interlocked-Vorgängen wechseln oder eine Sperre verwenden.

Orion Edwards
quelle
29
"Ich bin nicht ganz sicher ... ob Sie auch flüchtig mit dem Inkrement kombinieren müssen." Sie können nicht AFAIK kombiniert werden, da wir eine flüchtige nicht durch ref passieren können. Tolle Antwort übrigens.
Hosam Aly
41
Vielen Dank! Ihre Fußnote zu "Wofür flüchtig ist eigentlich gut" ist das, wonach ich gesucht und bestätigt habe, wie ich flüchtig verwenden möchte.
Jacques Bosch
7
Mit anderen Worten, wenn eine Variable als flüchtig deklariert wird, geht der Compiler davon aus, dass der Wert der Variable nicht jedes Mal gleich (dh flüchtig) bleibt, wenn Ihr Code darauf stößt. In einer Schleife wie: while (m_Var) {} und m_Var wird in einem anderen Thread auf false gesetzt. Der Compiler überprüft nicht einfach, was sich bereits in einem Register befindet, das zuvor mit dem Wert von m_Var geladen wurde, sondern liest den Wert aus m_Var aus nochmal. Dies bedeutet jedoch nicht, dass das Nichtdeklarieren von flüchtig die Schleife unendlich weiterlaufen lässt. Wenn Sie flüchtig angeben, wird dies nur dann garantiert, wenn m_Var in einem anderen Thread auf false gesetzt ist.
Zach Saw
35
@Zach Saw: Unter dem Speichermodell für C ++ ist flüchtig, wie Sie es beschrieben haben (grundsätzlich nützlich für Gerätezuordnungen und nicht viel anderes). Unter dem Speichermodell für die CLR (diese Frage ist mit C # gekennzeichnet) ist, dass flüchtig Speicherbarrieren um Lese- und Schreibvorgänge an diesen Speicherort einfügt. Speicherbarrieren (und spezielle gesperrte Variationen einiger Montageanweisungen) weisen Sie den Prozessor an, die Dinge nicht neu zu ordnen, und sie sind ziemlich wichtig ...
Orion Edwards
19
@ZachSaw: Ein flüchtiges Feld in C # verhindert, dass der C # -Compiler und der JIT-Compiler bestimmte Optimierungen vornehmen, die den Wert zwischenspeichern würden. Es gibt auch bestimmte Garantien dafür, in welcher Reihenfolge Lese- und Schreibvorgänge in mehreren Threads auftreten können. Als Implementierungsdetail kann dies durch Einführen von Speicherbarrieren beim Lesen und Schreiben geschehen. Die genaue garantierte Semantik ist in der Spezifikation beschrieben; Beachten Sie, dass die Spezifikation nicht garantiert, dass alle Threads eine konsistente Reihenfolge aller flüchtigen Schreib- und Lesevorgänge einhalten.
Eric Lippert
147

BEARBEITEN: Wie in den Kommentaren erwähnt, verwende ich diese Tage gerne Interlockedfür Fälle einer einzelnen Variablen, in denen es offensichtlich in Ordnung ist. Wenn es komplizierter wird, werde ich immer noch zum Sperren zurückkehren ...

Die Verwendung volatilehilft nicht, wenn Sie inkrementieren müssen, da Lesen und Schreiben separate Anweisungen sind. Ein anderer Thread kann den Wert nach dem Lesen, aber vor dem Zurückschreiben ändern.

Persönlich sperre ich fast immer nur - es ist einfacher, auf eine Weise richtig zu machen, die offensichtlich richtig ist, als entweder Volatilität oder Interlocked.Increment. Für mich ist sperrenfreies Multithreading etwas für echte Threading-Experten, von denen ich keiner bin. Wenn Joe Duffy und sein Team nette Bibliotheken erstellen, die Dinge parallelisieren, ohne so viel zu sperren wie etwas, das ich bauen würde, ist das fabelhaft, und ich werde es sofort verwenden - aber wenn ich das Threading selbst mache, versuche ich es halte es einfach.

Jon Skeet
quelle
16
+1, damit ich ab sofort die sperrfreie Codierung vergesse.
Xaqron
5
Lock-Free-Codes sind definitiv nicht wirklich sperrenfrei, da sie irgendwann gesperrt werden - ob auf (FSB-) Bus- oder InterCPU-Ebene, es gibt immer noch eine Strafe, die Sie zahlen müssten. Das Sperren auf diesen niedrigeren Ebenen ist jedoch im Allgemeinen schneller, solange Sie die Bandbreite des Sperrens nicht sättigen.
Zach Saw
2
Es ist nichts falsch mit Interlocked, es ist genau das, wonach Sie suchen und schneller als eine vollständige Sperre ()
Jaap
5
@Jaap: Ja, heutzutage würde ich Interlocked für einen echten Einzelzähler verwenden. Ich möchte einfach nicht herumspielen und versuchen, Interaktionen zwischen mehreren sperrfreien Aktualisierungen von Variablen herauszufinden .
Jon Skeet
6
@ZachSaw: Ihr zweiter Kommentar besagt, dass ineinandergreifende Operationen irgendwann "sperren"; Der Begriff "Sperre" impliziert im Allgemeinen, dass eine Aufgabe die ausschließliche Kontrolle über eine Ressource über einen unbegrenzten Zeitraum behalten kann. Der Hauptvorteil der sperrenfreien Programmierung besteht darin, dass die Gefahr vermieden wird, dass Ressourcen unbrauchbar werden, wenn die Eigentümeraufgabe überlagert wird. Die von der verriegelten Klasse verwendete Bussynchronisation ist nicht nur "im Allgemeinen schneller" - auf den meisten Systemen hat sie eine begrenzte Worst-Case-Zeit, Sperren hingegen nicht.
Supercat
44

" volatile" ersetzt nicht Interlocked.Increment! Es wird nur sichergestellt, dass die Variable nicht zwischengespeichert, sondern direkt verwendet wird.

Das Inkrementieren einer Variablen erfordert tatsächlich drei Operationen:

  1. lesen
  2. Zuwachs
  3. schreiben

Interlocked.Increment führt alle drei Teile als eine einzige atomare Operation aus.

Michael Damatov
quelle
4
Anders gesagt, Interlocked-Änderungen sind vollständig eingezäunt und als solche atomar. Flüchtige Mitglieder sind nur teilweise eingezäunt und daher nicht als fadensicher garantiert.
JoeGeeky
1
Stellt volatileeigentlich nicht sicher, dass die Variable nicht zwischengespeichert ist. Es gibt nur Einschränkungen, wie es zwischengespeichert werden kann. Zum Beispiel kann es immer noch im L2-Cache der CPU zwischengespeichert werden, da diese in der Hardware kohärent sind. Es kann immer noch bevorzugt werden. Schreibvorgänge können weiterhin in den Cache gestellt werden und so weiter. (Was ich denke, war das, worauf Zach hinaus wollte.)
David Schwartz
42

Sie suchen entweder nach einem gesperrten oder einem ineinandergreifenden Inkrement.

Volatile ist definitiv nicht das, wonach Sie suchen - es weist den Compiler einfach an, die Variable so zu behandeln, als würde sie sich ständig ändern, selbst wenn der aktuelle Codepfad es dem Compiler ermöglicht, das Lesen aus dem Speicher anderweitig zu optimieren.

z.B

while (m_Var)
{ }

Wenn m_Var in einem anderen Thread auf false gesetzt ist, aber nicht als flüchtig deklariert ist, kann der Compiler eine Endlosschleife erstellen (dies bedeutet jedoch nicht, dass dies immer der Fall ist), indem er sie mit einem CPU-Register vergleicht (z. B. EAX, weil dies der Fall war) in was m_Var von Anfang an abgerufen wurde) anstatt einen weiteren Lesevorgang an den Speicherort von m_Var zu senden (dies kann zwischengespeichert werden - wir wissen es nicht und es ist uns egal und das ist der Punkt der Cache-Kohärenz von x86 / x64). Alle früheren Beiträge von anderen, die die Neuordnung von Anweisungen erwähnt haben, zeigen einfach, dass sie x86 / x64-Architekturen nicht verstehen. Flüchtig nichtStellen Sie Lese- / Schreibbarrieren aus, wie in den früheren Beiträgen impliziert: "Es verhindert eine Neuordnung". Dank des MESI-Protokolls ist garantiert, dass das von uns gelesene Ergebnis auf allen CPUs immer gleich ist, unabhängig davon, ob die tatsächlichen Ergebnisse in den physischen Speicher verschoben wurden oder sich einfach im Cache der lokalen CPU befinden. Ich werde nicht zu weit in die Details gehen, aber seien Sie versichert, dass Intel / AMD im Falle eines Fehlers wahrscheinlich einen Prozessorrückruf ausgeben würde! Dies bedeutet auch, dass wir uns nicht um die Ausführung außerhalb der Reihenfolge usw. kümmern müssen. Die Ergebnisse werden garantiert immer in der richtigen Reihenfolge in den Ruhestand versetzt - ansonsten sind wir vollgestopft!

Bei Interlocked Increment muss der Prozessor ausgehen, den Wert von der angegebenen Adresse abrufen, dann inkrementieren und zurückschreiben - und das alles, während er das ausschließliche Eigentum an der gesamten Cache-Zeile (lock xadd) hat, um sicherzustellen, dass keine anderen Prozessoren Änderungen vornehmen können dessen Wert.

Mit volatile erhalten Sie immer noch nur eine Anweisung (vorausgesetzt, die JIT ist effizient, wie sie sollte) - inc dword ptr [m_Var]. Der Prozessor (cpuA) fordert jedoch nicht das ausschließliche Eigentum an der Cache-Zeile an, während er alles tut, was er mit der verriegelten Version getan hat. Wie Sie sich vorstellen können, bedeutet dies, dass andere Prozessoren einen aktualisierten Wert nach dem Lesen durch cpuA zurück in m_Var schreiben können. Anstatt den Wert jetzt zweimal zu erhöhen, erhalten Sie nur einmal.

Hoffe, das klärt das Problem.

Weitere Informationen finden Sie unter "Verstehen der Auswirkungen von Low-Lock-Techniken in Multithread-Apps" - http://msdn.microsoft.com/en-au/magazine/cc163715.aspx

ps Was hat diese sehr späte Antwort ausgelöst? Alle Antworten waren in ihrer Erklärung so offensichtlich falsch (insbesondere die als Antwort gekennzeichnete), dass ich sie nur für alle anderen, die dies lesen, klären musste. zuckt die Achseln

pps Ich gehe davon aus, dass das Ziel x86 / x64 und nicht IA64 ist (es hat ein anderes Speichermodell). Beachten Sie, dass die ECMA-Spezifikationen von Microsoft insofern durcheinander sind, als sie das schwächste Speichermodell anstelle des stärksten angeben (es ist immer besser, das stärkste Speichermodell zu verwenden, damit es plattformübergreifend konsistent ist - andernfalls würde Code auf x86 / 24-7 ausgeführt x64 läuft möglicherweise überhaupt nicht auf IA64, obwohl Intel ein ähnlich starkes Speichermodell für IA64 implementiert hat - Microsoft gab dies selbst zu - http://blogs.msdn.com/b/cbrumme/archive/2003/05/17/51445.aspx .

Zach Saw
quelle
3
Interessant. Können Sie sich darauf beziehen? Ich würde dies gerne abstimmen, aber das Posten mit einer aggressiven Sprache 3 Jahre nach einer hoch bewerteten Antwort, die mit den Ressourcen übereinstimmt, die ich gelesen habe, erfordert einen etwas greifbareren Beweis.
Steven Evers
Wenn Sie angeben können, auf welchen Teil Sie verweisen möchten, würde ich gerne einige Dinge von irgendwoher ausgraben (ich bezweifle sehr, dass ich Geschäftsgeheimnisse von x86 / x64-Anbietern preisgegeben habe, daher sollten diese im Wiki Intel leicht verfügbar sein PRMs (Programmierreferenzhandbücher), MSFT-Blogs, MSDN oder ähnliches) ...
Zach Saw
2
Warum jemand verhindern möchte, dass die CPU zwischengespeichert wird, ist mir ein Rätsel. Die gesamte Fläche (definitiv nicht zu vernachlässigen in Größe und Kosten), die für die Durchführung der Cache-Kohärenz vorgesehen ist, wird in diesem Fall vollständig verschwendet ... Wenn Sie keine Cache-Kohärenz wie eine Grafikkarte, ein PCI-Gerät usw. benötigen, würden Sie diese nicht festlegen eine Cache-Zeile zum Durchschreiben.
Zach Saw
4
Ja, alles, was Sie sagen, ist, wenn nicht 100%, mindestens 99% der Marke. Diese Seite ist (meistens) ziemlich nützlich, wenn Sie sich in der Eile der Entwicklung befinden, aber leider ist die Genauigkeit der Antworten, die dem (Spiel der) Stimmen entsprechen, nicht vorhanden. Im Grunde genommen können Sie im Stackoverflow ein Gefühl dafür bekommen, was das populäre Verständnis der Leser ist, nicht was es wirklich ist. Manchmal sind die besten Antworten nur Kauderwelsch - Mythen der Art. Und leider ist es das, was die Leute hervorbringt, die beim Lösen des Problems auf die Lektüre stoßen. Es ist jedoch verständlich, dass niemand alles wissen kann.
user1416420
1
@BenVoigt Ich könnte weiter und über alle Architekturen antworten, auf denen .NET läuft, aber das würde ein paar Seiten dauern und ist definitiv nicht für SO geeignet. Es ist weitaus besser, Menschen auf der Grundlage des am häufigsten verwendeten .NET-zugrunde liegenden Hardware-Mem-Modells zu schulen, als eines, das willkürlich ist. Und mit meinen Kommentaren "überall" korrigierte ich die Fehler, die die Leute bei der Annahme machten, dass der Cache geleert / ungültig gemacht wurde usw. Sie machten Annahmen über die zugrunde liegende Hardware, ohne anzugeben, welche Hardware.
Zach Saw
16

Verriegelte Funktionen werden nicht gesperrt. Sie sind atomar, was bedeutet, dass sie während des Inkrements ohne die Möglichkeit eines Kontextwechsels abgeschlossen werden können. Es besteht also keine Möglichkeit eines Deadlocks oder Wartens.

Ich würde sagen, dass Sie es immer einer Sperre und einem Inkrement vorziehen sollten.

Volatile ist nützlich, wenn Sie Schreibvorgänge in einem Thread benötigen, um in einem anderen gelesen zu werden, und wenn Sie möchten, dass der Optimierer die Vorgänge für eine Variable nicht neu anordnet (weil in einem anderen Thread Dinge geschehen, von denen der Optimierer nichts weiß). Es ist eine orthogonale Wahl, wie Sie inkrementieren.

Dies ist ein wirklich guter Artikel, wenn Sie mehr über sperrenfreien Code und die richtige Vorgehensweise beim Schreiben erfahren möchten

http://www.ddj.com/hpc-high-performance-computing/210604448

Lou Franco
quelle
11

lock (...) funktioniert, blockiert jedoch möglicherweise einen Thread und kann zu einem Deadlock führen, wenn anderer Code dieselben Sperren auf inkompatible Weise verwendet.

Interlocked. * Ist der richtige Weg, dies zu tun ... viel weniger Overhead, da moderne CPUs dies als Grundelement unterstützen.

flüchtig allein ist nicht korrekt. Ein Thread, der versucht, einen geänderten Wert abzurufen und dann zurückzuschreiben, kann immer noch einen Konflikt mit einem anderen Thread haben, der dasselbe tut.

Rob Walker
quelle
8

Ich habe einige Tests durchgeführt, um zu sehen, wie die Theorie tatsächlich funktioniert: kennethxu.blogspot.com/2009/05/interlocked-vs-monitor-performance.html . Mein Test konzentrierte sich mehr auf CompareExchnage, aber das Ergebnis für Increment ist ähnlich. Interlocked ist in einer Umgebung mit mehreren CPUs nicht schneller erforderlich. Hier ist das Testergebnis für Increment auf einem 2 Jahre alten 16-CPU-Server. Beachten Sie jedoch, dass der Test auch das sichere Ablesen nach dem Erhöhen umfasst, was in der realen Welt typisch ist.

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunIncrement         (ns):   8355 Average,   8302 Minimal,   8409 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):   7077 Average,   6843 Minimal,   7243 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunIncrement         (ns):   4319 Average,   4319 Minimal,   4321 Maxmial
    MonitorVolatileAtomic.RunIncrement         (ns):    933 Average,    802 Minimal,   1018 Maxmial
Kenneth Xu
quelle
Das Codebeispiel, das Sie getestet haben, war allerdings soooo trivial - es macht wirklich keinen Sinn, es so zu testen! Am besten verstehen Sie, was die verschiedenen Methoden tatsächlich tun, und verwenden die entsprechende Methode, die auf dem von Ihnen verwendeten Verwendungsszenario basiert.
Zach Saw
@Zach, wie hier über das Szenario diskutiert wurde, einen Zähler threadsicher zu erhöhen. Welches andere Nutzungsszenario haben Sie sich vorgestellt oder wie würden Sie es testen? Danke für den Kommentar Übrigens.
Kenneth Xu
Der Punkt ist, es ist ein künstlicher Test. Sie werden nicht den gleichen Ort wie in einem realen Szenario hämmern. Wenn ja, dann hat der FSB einen Engpass (wie in Ihren Serverboxen gezeigt). Wie auch immer, schau dir meine Antwort in deinem Blog an.
Zach Saw
2
Ich schaue noch einmal zurück. Wenn der wahre Engpass bei FSB liegt, sollte die Monitorimplementierung denselben Engpass feststellen. Der wirkliche Unterschied besteht darin, dass Interlocked viel wartet und erneut versucht, was bei der Hochleistungszählung zu einem echten Problem wird. Zumindest hoffe ich, dass mein Kommentar die Aufmerksamkeit auf sich zieht, dass Interlocked nicht immer die richtige Wahl zum Zählen ist. Die Tatsache, dass die Leute nach Alternativen suchen, hat dies gut erklärt. Sie benötigen einen langen Addierer gee.cs.oswego.edu/dl/jsr166/dist/jsr166edocs/jsr166e/…
Kenneth Xu
8

Ich stimme der Antwort von Jon Skeet zu und möchte die folgenden Links für alle hinzufügen, die mehr über "volatile" und Interlocked erfahren möchten:

Atomizität, Volatilität und Unveränderlichkeit sind unterschiedlich, Teil eins - (Eric Lipperts Fabulous Adventures In Coding)

Atomizität, Flüchtigkeit und Unveränderlichkeit sind unterschiedlich, Teil zwei

Atomizität, Flüchtigkeit und Unveränderlichkeit sind unterschiedlich, Teil drei

Sayonara Volatile - (Wayback Machine-Schnappschuss von Joe Duffys Weblog, wie er 2012 erschien)

zihotki
quelle
3

Ich mag erwähnt in den anderen Antworten den Unterschied zwischen hinzuzufügen volatile, Interlockedund lock:

Das Schlüsselwort volatile kann auf Felder dieses Typs angewendet werden :

  • Referenztypen.
  • Zeigertypen (in einem unsicheren Kontext). Beachten Sie, dass der Zeiger selbst zwar flüchtig sein kann, das Objekt, auf das er zeigt, jedoch nicht. Mit anderen Worten, Sie können einen "Zeiger" nicht als "flüchtig" deklarieren.
  • Einfache Typen wie sbyte, byte, short, ushort, int, uint, char, float, und bool.
  • Ein ENUM - Typ mit einem der folgenden Grundtypen: byte, sbyte, short, ushort, intoder uint.
  • Generische Typparameter, die als Referenztypen bekannt sind.
  • IntPtrund UIntPtr.

Andere Typen , einschließlich doubleund long, können nicht als "flüchtig" markiert werden, da beim Lesen und Schreiben in Felder dieser Typen nicht garantiert werden kann, dass sie atomar sind. Verwenden Sie die InterlockedKlassenmitglieder oder den Zugriff mithilfe der lockAnweisung , um den Multithread-Zugriff auf diese Feldtypen zu schützen .

Vadim S.
quelle