Frage

Ich weiß, dass das .NET-Speicher-Modell (auf dem .NET Framework, nicht kompakt / Mikro / Silverlight / Mono / xna / was-hat-Sie) garantiert, dass für bestimmte Arten (insbesondere primitiven Zahlen und Referenzen) Operationen waren garantiert atomar sein.

Außerdem glaube ich, dass die x86 / x64-Test-and-Befehlssatz (und Interlocked.CompareExchange) tatsächlich die globale Speicherstelle verweist, so dass, wenn es eine andere Interlocked.CompareExchange gelingt es den neuen Wert sehen würde.

Schließlich glaube ich, dass das volatile Schlüsselwort ist eine Anweisung an die Compiler zu propagieren liest und schreibt so schnell wie möglich und nicht Neuordnungs Operationen über diese Variable (rechts?).

Dies führt zu einigen Fragen:

  1. Sind meine Überzeugungen oben richtig?
  2. Interlocked.Read keine Überlastung für int hat, nur für Long-Positionen (die 2 WORDs sind und somit in der Regel nicht atomar lesen). Ich habe immer angenommen, dass das .NET-Speichermodell gewährleistet, dass der neueste Wert gesehen werden würde, wenn ints / Referenzen, jedoch mit Prozessor-Caches zu lesen, Register usw. Ich fange an zu sehen dies nicht möglich sein kann. So ist es eine Möglichkeit, um die Variable zu zwingen, wieder geholt zu werden?
  3. Ist ausreichend flüchtig, das obige Problem für ganze Zahlen und Referenzen zu lösen?
  4. Auf x86 / x64 kann ich davon ausgehen, dass ...

Wenn es zwei globale Integer-Variablen x und y, die beide auf 0 initialisiert, dass, wenn ich schreibe:

x = 1;
y = 2;

, dass kein Gewinde x = 0 sehen und y = 2 (das heißt die Schreibvorgänge werden in der Reihenfolge auftreten). Ist diese Änderung, wenn sie flüchtig sind?

War es hilfreich?

Lösung

  • Nur liest und schreibt an Variablen, die 32 Bit breit höchstens sind (und 64 Bit breit auf x64-Systemen) sind atomar. All dies bedeutet, ist, dass Sie nicht ein lesen int und einen Halb geschriebenen Wert. Es bedeutet nicht, Arithmetik ist atomar.
  • Die ineinandergreifenden Operationen auch als Speicherbarrieren wirken, also ja, Interlocked.CompareExchange den aktualisierten Wert sehen.
  • Siehe dieser Seite . Volatile bedeutet nicht bestellt. Einige Compiler können wählen, nicht zu Nachbestellung Operationen an flüchtigen Variablen, aber die CPU ist frei Nachbestellung. Wenn Sie die CPU aus Nachbestellung Anweisungen stoppen wollen, verwenden Sie ein (voll) Speichersperre.
  • Die Speichermodell sorgt dafür, dass Lesen und Schreiben sind atomar, und mit den flüchtigen Schlüsselwort sorgt dafür, dass liest, wird immer aus dem Gedächtnis kommen, nicht aus einem Register. So Sie wird finden Sie in der neuesten Wert. Dies liegt daran, x86-CPUs den Cache ungültig machen, wenn zutreffend - siehe dieses und diese . Auch finden Sie unter InterlockedCompareExchange64 wie atomar lesen 64-Bit-Werte.
  • Und schließlich die letzte Frage. Die Antwort ist ein Thread könnte in der Tat siehe x = 0 und y = 2, und das flüchtige Schlüsselwort das nicht ändern, da der CPU frei Nachbestellung Anweisungen ist. Sie benötigen eine Speicherbarriere.

Zusammenfassung:

  1. Der Compiler ist frei Nachbestellung Anweisungen.
  2. Die CPU ist frei Nachbestellung Anweisungen.
  3. Wortgroßen Lese- und Schreibatom sind. Arithmetik und andere Operationen sind nicht atomar, weil sie eine Lese-, Rechen-, dann schreiben einzubeziehen.
  4. Wort-Größe liest aus dem Speicher wird immer den neuesten Wert abrufen. Aber die meiste Zeit wissen Sie nicht, ob Sie tatsächlich aus dem Speicher zu lesen.
  5. Eine vollständige Speicher Schranke stoppt (1) und (2). Die meisten Compiler ermöglicht es Ihnen (1) für sich zu beenden.
  6. Die flüchtige Schlüsselwort stellen sicher, Sie lesen aus dem Speicher - (4).
  7. Die ineinandergreifenden Operationen (die Sperre Präfix) erlauben mehrere Operationen atomar zu sein. Zum Beispiel lesen a + write (InterlockedExchange). Oder ein Lese + Vergleichen + write (InterlockedCompareExchange). Sie wirken auch als Speicherbarrieren, so (1) und (2) gestoppt. Sie schreiben immer in dem Speicher (natürlich), so (4) gewährleistet ist.

Andere Tipps

kam in diesem alten Thread. Die Antworten von Hans und wj32 sind alle richtig mit Ausnahme des Teils in Bezug auf volatile.

Im Einzelnen Deine Frage zu

  

Auf x86 / x64 kann ich davon ausgehen, dass ... Wenn   gibt es zwei globale Integer-Variablen   x und y, die beide auf 0 initialisiert, wenn   Ich schreibe:   x = 1; y = 2;

     

Das Gewinde NO sehen   x = 0 und y = 2 (das heißt, die Schreibvorgänge   auftreten, in dieser Reihenfolge). Ist diese Änderung, wenn   sie sind flüchtig?

Wenn y flüchtig ist, das Schreiben in x Garantie ist vor dem Schreibvorgang auf y passieren, deshalb wird kein Thread jemals x = 0 und y = 2 sehen. Das ist, weil das Schreiben in einem flüchtigen Variable hat die „Freigabe semantische“ (logisch äquivalent zu der Emission eines Freisetzungs fence), das heißt alle Lese / Schreib-Anweisungen, bevor er sich nicht bewegt es passieren. (Dies bedeutet, dass wenn x flüchtig ist aber y nicht, könnte man immer noch die unerwartete x = 0 und y = 2 sehen.) Siehe Beschreibung & Codebeispiel in dem C # spec für weitere Details.

Nein, das Schlüsselwort volatile und die Unteilbarkeit Garantie ist viel zu schwach. Sie benötigen eine Speicherbarriere, um sicherzustellen. Sie können eine explizit mit dem Thread.MemoryBarrier () -Methode erhalten.

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