powszechną praktyką w celu uniknięcia warunków wyścigu (w wielowątkowych aplikacjach) podczas wyzwalania zdarzeń jest następująca:Thread Safe Event zwraca
EventHandler<EventArgs> temp = SomeEvent;
if (temp != null) temp(e);
"Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible."
Problem (według książki) jest to, że „ten kod może być zoptymalizowane Jeśli tak się stanie, ta wersja kodu jest identyczna z pierwszą wersją, więc wyjątek NullReferenceException jest nadal możliwy "
Według CLR za pośrednictwem C#, oto lepszy sposób na zmusić kompilator do skopiowania wskaźnika zdarzenia.
virtual void OnNewMail(NewMailEventArgs e)
{
EventHandler<NewMailEventArgs> temp =
Interlocked.CompareExchange(ref NewMail, null, null);
if (temp != null)
temp(this, e);
}
Tutaj CompareExchange zmienia odniesienie NewMail NULL jeśli jest nieważna i nie zmienia NewMail jeśli nie jest null. Innymi słowy, CompareExchange w ogóle nie zmienia wartości w NewMail, ale zwraca wartość wewnątrz NewMail w sposób atomowy i wątkowy. Richter, Jeffrey (2010-02-12). CLR za pośrednictwem C# (s. 265). OReilly Media - A. Kindle Edition.
Jestem na platformie .Net 4.0, i nie jestem pewien, jak to możliwe, ponieważ Interlocked.CompareExchange oczekuje odniesienia do lokalizacji, a nie odniesienia do zdarzenia.
Albo jest błąd w książce, albo źle to zinterpretowałem. Czy ktoś zaimplementował tę metodę? A może masz lepszy sposób na zapobieganie wyścigowi?
UPDATE
to był mój błąd, kod iterlocked działa. Właśnie podałem niewłaściwy casting, ale według Bradley'a (poniżej) nie jest to konieczne w .net 2.0 i wyżej w oknach.
http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx może być interesujące – Vlad