Thread Safe Event Calls

8

Um Race-Bedingungen (in Multithread-Anwendungen) beim Auslösen von Ereignissen zu vermeiden, ist dies gängige Praxis:

%Vor%

Das Problem (laut Buch) ist, dass "dieser Code vom Compiler optimiert werden könnte, um die lokale temporäre Variable vollständig zu entfernen. Wenn dies geschieht, ist diese Version des Codes identisch mit der ersten Version, also eine NullReferenceException." immer noch möglich "

Laut CLR über C # ist es hier eine bessere Möglichkeit, den Compiler zum Kopieren des Ereigniszeigers zu zwingen.

%Vor%
  

Hier ändert CompareExchange den NewMail-Verweis auf null, wenn es null ist, und ändert NewMail nicht, wenn es nicht null ist. Mit anderen Worten, CompareExchange ändert den Wert in NewMail überhaupt nicht, aber es gibt den Wert innerhalb von NewMail auf atomare, thread-sichere Weise zurück.       Richter, Jeffrey (2010-02-12). CLR über C # (S. 265). OReilly Media - A. Kindle-Ausgabe.

Ich bin auf .NET 4.0-Framework und nicht sicher, wie dies möglicherweise funktionieren kann, da Interlocked.CompareExchange eine Referenz auf einen Speicherort erwartet, nicht eine Referenz auf ein Ereignis.

Entweder ist ein Fehler im Buch oder ich habe ihn falsch interpretiert. Hat jemand diese Methode implementiert? Oder haben Sie hier einen besseren Weg, um die Rennbedingungen zu verhindern?

AKTUALISIEREN

Es war mein Fehler, der iterlocked Code funktioniert. Ich hatte gerade falsch Casting angegeben, aber laut Bradley (unten) ist es nicht notwendig in .net 2.0 und höher auf Windows.

    
Sonic Soul 22.06.2012, 15:19
quelle

4 Antworten

7

Der Compiler (oder JIT) darf das if/temp weg (in CLR 2.0 und höher) nicht optimieren; Das CLR 2.0-Speichermodell erlaubt keine Lesevorgänge vom Heap (Regel # 2).

Somit kann MyEvent nicht ein zweites Mal gelesen werden; Der Wert von temp muss in der if -Anweisung gelesen werden.

Siehe meinen Blogbeitrag für eine ausführliche Diskussion dieser Situation und eine Erklärung warum Das Standardmuster ist in Ordnung.

Wenn Sie jedoch mit einer Nicht-Microsoft-CLR (z. B. mono) arbeiten, die nicht die CLR 2.0-Speichermodellgarantien bietet (sondern nur dem ECMA-Speichermodell entspricht) oder auf Itanium läuft (was hat ein notorisch schwaches Hardware-Speichermodell), Sie benötigen Code wie Richter, um eine mögliche Race-Bedingung zu beseitigen.

In Bezug auf Ihre Frage zu Interlocked.CompareExchange ist die Syntax public event EventHandler<NewMailEventArgs> NewMail nur C # syntaktischer Zucker zum Deklarieren eines privaten Feldes vom Typ EventHandler<NewMailEventArgs> und ein öffentliches Ereignis mit den Methoden add und remove . Der Interlocked.CompareExchange -Aufruf liest den Wert des privaten EventHandler<NewMailEventArgs> -Feldes, also kompiliert und arbeitet dieser Code, wie Richter beschreibt; Es ist nur unnötig in der Microsoft CLR.

    
Bradley Grainger 22.06.2012, 15:24
quelle
3

Nun ist dies nur eine unvollständige Antwort auf Ihre Frage, da ich Interlocked.CompareExchange nicht kommentieren kann, aber ich denke, dass diese Information nützlich sein könnte.

  

Das Problem ist, dass der Compiler das if / temp weg optimieren kann,

Nun, nach CLR über C # (S. 264-265)

  

[Der] Code könnte vom Compiler optimiert werden, um die lokale [...] Variable vollständig zu entfernen. Wenn dies geschieht, ist diese Version des Codes identisch mit der [Version, die das Ereignis zweimal referenziert], sodass eine NullReferenceException weiterhin möglich ist.

Es ist also möglich , aber es ist wichtig zu wissen, dass der JIT-Compiler von Microsoft nicht jemals ist Optimiere die lokale Variable. Dies könnte sich zwar ändern, ist aber unwahrscheinlich, weil es wahrscheinlich viele Anwendungen zerstören würde.

Dies liegt daran, dass .Net ein starkes Speichermodell hat: Ссылка

  

Lese- und Schreibvorgänge können nicht eingeführt werden.

und

  

Das Modell lässt jedoch keine Lesevorgänge zu, da dies bedeuten würde, dass ein Wert aus dem Speicher abgerufen werden muss und dass sich der Speichercode mit niedrigem Code ändern könnte.

Allerdings könnte Mono, das einem viel schwächeren Speichermodell folgt, dies optimieren lokale Variable entfernt.

Fazit: Wenn Sie nicht beabsichtigen, Mono zu verwenden, machen Sie sich keine Sorgen.

Und selbst dann kann dieses Verhalten mit volatilen Deklarationen unterdrückt werden.

    
caesay 22.06.2012 15:43
quelle
1

Ich nehme an, du vermisst die Interpretation. Location bedeutet nur ein Zeiger auf Objektreferenz [ msdn version: Das Zielobjekt, das mit comparand verglichen und möglicherweise ersetzt wird. ]. Der folgende Code funktioniert in. NET 4.0

%Vor%     
s_nair 22.06.2012 15:39
quelle
0

Wenn Sie sich die erzeugte IL ansehen, werden Sie sehen, dass die Methode so heißt

%Vor%

siehe ldsflda - mein Ereignis ist statisch, aber es lädt die Adresse eines Feldes . Dieses Feld ist das automatisch generierte Delegate-Feld, das der Compiler für jedes Ereignis generiert.

Das Feld ist wie folgt definiert:

%Vor%     
Bond 22.06.2012 15:45
quelle

Tags und Links