Angenommen, Sie haben eine Eigenschaft public Foo Bar { get; }
, die Sie initialisieren möchten. Ein solcher Ansatz könnte darin bestehen, die Klasse Interlocked
zu verwenden, die Atomizität für bestimmte Abfolgen von Operationen garantiert (z. B. inkrementieren, hinzufügen, vergleichen, austauschen). Sie könnten dies tun:Ist es richtig, reguläre Lesevorgänge in einem Feld durchzuführen, das von Interlocked.CompareExchange initialisiert wurde?
private Foo _bar;
public Foo Bar
{
get
{
// Initial check, necessary so we don't call new Foo() every time when invoking the method
if (_bar == null)
{
// In the unlikely case 2 threads come in here
// at the same time, new Foo() will simply be called
// twice, but only one of them will be set to _bar
Interlocked.CompareExchange(ref _bar, new Foo(), null);
}
return _bar;
}
}
Es gibt viele Orte, die diesen Ansatz zur Lazy-Initialisierung, z. this blog und places in the .NET Framework itself.
Meine Frage ist, sollte das Lesen von _bar
nicht flüchtig sein? Zum Beispiel könnte Thread 1 CompareExchange
genannt haben und den Wert _bar
setzen, aber diese Änderung wäre für Thread 2 nicht sichtbar, da (wenn ich this question richtig verstanden habe) der Wert _bar
als null zwischengespeichert wurde, und es könnte enden ruft Interlocked.CompareExchange
erneut auf, obwohl Thread 1 bereits _bar
eingestellt hat. Sollte also nicht _bar
als flüchtig gekennzeichnet werden, um dies zu verhindern?
Kurz gesagt, ist this approach oder this approach zu lazy Initialisierung korrekt? Warum wird Volatile.Read
(was hat den gleichen Effekt wie das Markieren einer Variablen volatile und Lesen von ihm) in einem Fall verwendet, aber nicht die andere?
TL bearbeiten; DR: Wenn ein Thread, den Wert eines Feldes über Interlocked.CompareExchange
aktualisiert wird, dass aktualisierte Wert zu anderen Threads sofort sichtbar Durchführen einer nichtflüchtigen liest des Feldes?
Danke für die Antwort! Über den ersten Punkt: Wenn der Thread, der ein reguläres Lesen der Variablen durchführt, dies als null sieht und das Ergebnis zwischenspeichert, wird _bar nicht zurückgegeben und der zwischengespeicherte Wert (null) zurückgegeben, aka return null? (Auch .NET ist auf ARM-Prozessoren, beispielsweise Windows Phone, sehr verbreitet. Deshalb mache ich mir Sorgen.) –
Entschuldigung, können Sie das klären? Nehmen Sie an, dass die Variable registriert ist und so den alten 'null'-Wert zurückgibt, selbst nachdem' CompareExchange() 'aufgerufen wurde? Wenn dem so ist, glaube ich nicht, dass das legal ist. Selbst wenn die Variable registriert ist, muss der Compiler nach dem Aufruf von 'CompareExchange()' den Wert unbedingt erneut lesen, bevor er zurückkehrt, weil 'CompareExchange()' seinen Wert möglicherweise geändert hat. Wenn du das nicht meinst, könntest du genauer sagen, was du meinst? –
Das war was ich meinte. Um also klar zu sein, dass ein Thread den Wert eines Feldes, das von einem anderen Thread mit "CompareExchange" gesetzt wurde, nicht sieht, wenn er "CompareExchange" selbst aufruft, muss er den Wert beim nächsten Lesen erneut aus dem Hauptspeicher laden? –