2012-07-16 12 views
11

meine cusom EventArgs verwenden wie:EventHandler <TEventArgs> Gewindesicherheit in C#?

public event EventHandler<MyEventArgs> SampleEvent; 

von msdn zB:

public class HasEvent 
{ 
// Declare an event of delegate type EventHandler of 
// MyEventArgs. 

    public event EventHandler<MyEventArgs> SampleEvent; 

    public void DemoEvent(string val) 
    { 
    // Copy to a temporary variable to be thread-safe. 
     EventHandler<MyEventArgs> temp = SampleEvent; 
     if (temp != null) 
      temp(this, new MyEventArgs(val)); 
    } 
} 

Ich habe Frage:

1) an der markierten Code suchen:

enter image description here

ich nicht einen Grund, warum es zu einem anderen param (in Bezug auf Themen)

kopiert werden sollte, da wir die event keyowrd haben, kann niemand seine Aufrufliste (kein Außenseiter Code zur Klasse ich meine) berühren

2) Wenn im nicht irre, die DemoEvent Funktion sollte virtuell sein, so kann es in Unterklassen überschrieben werden ... (ich bin sicher, es irgendwo gesehen ive)

das merkwürdige ist, dass ReSharper pflegt auch virtuelle hinzufügen :

so, wenn ich diesen Code haben:

enter image description here

es schlägt mich:

enter image description here

und wenn ich drücke es:

enter image description here

so wieder mein Fragen:

1) Was ist das Szenario, das diese Linie EventHandler<MyEventArgs> temp = SampleEvent; löst, in Bezug auf Thread Safty?

2) darf nicht die Funktion virtual sein? (Ich bin sicher, ich sehe dieses Muster mit virtuellen)

+0

Resharper ist nur eine Sicht auf richtig und falsch. Es ist kein absolutes – podiluska

+1

Beachten Sie, dass es tatsächlich zwei Race-Bedingungen gibt. Diese Codeänderung behebt nur eines dieser Rennen. Siehe Eric Lipperts ausgezeichneten Artikel [Events and Races] (http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx) für eine vollständige Erklärung. – Brian

+1

mögliches Duplikat von [C# Ereignisse und Thread-Sicherheit] (http://stackoverflow.com/questions/786383/c-sharp-events-and-thread-safety) –

Antwort

10

Was ist das Szenario, das diese Zeile EventHandler temp = SampleEvent; wird gelöst, in Bezug auf Thread-Sicherheit?

Stellen Sie sich vor Sie tun dies:

if (SampleEvent != null) 
    SampleEvent(this, new MyEventArgs()); 

Wenn ein anderer Thread den Ereignishandler trennen, nachdem die, wenn aber vor dem Aufruf dann werden Sie versuchen, einen null Delegierten rufen (und Sie werden eine bekommen Ausnahme).

sollte nicht die Funktion virtuell sein?(Ich bin sicher, dieses Muster ive mit virtuellen gesehen)

Ja, wenn die Klasse ist nicht sealed dann sollten Sie diese Funktion markieren virtual (es ist nicht obligatorisch ist, aber es ist ein gut angenommen Muster).

EDIT

 
Time Thread 1          Thread 2 
1             obj.SampleEvent += MyHandler; 
2  if (SampleEvent != null)      
3  {           obj.SampleEvent -= MyHandler; 
4   SampleEvent(this, new MyEventArgs()); 
5  } 

In diesem Fall zum Zeitpunkt 4 Sie einen null Delegierten nennen und es wird eine NullReferenceException werfen. Nun sieh mal diesen Code:

 
Time Thread 1          Thread 2 
1             obj.SampleEvent += MyHandler; 
2  var sampleEvent = SampleEvent; 
3  if (sampleEvent != null)      
4  {           obj.SampleEvent -= MyHandler; 
5   sampleEvent(this, new MyEventArgs()); 
6  } 

jetzt zum Zeitpunkt 5 rufen Sie sampleEvent und es hält den alten Inhalt SampleEvent, in diesem Fall ist es keine Ausnahme ausgelöst wird (aber es wird MyHandler auch wenn es nennen wurde durch den zweiten Thread entfernt).

+0

Ich denke, das bedeutet, dass einige Abonnenten immer noch ein Ereignis erhalten können auslösen, auch nachdem sie abgelöst worden waren. – apokryfos

+2

@apokryfos ja, es ist wahr. Eine bessere Lösung sollte eine benutzerdefinierte Ereignisimplementierung beinhalten, aber selbst in diesem Fall können Sie unerwünschtes Verhalten bei Threads und Sperren erhalten. Es gibt keine (AFAIK) gute und allgemeine Lösung, wahrscheinlich, wenn Sie diese Probleme vermeiden müssen, müssen Sie einen langen/langsamen benutzerdefinierten Code sowohl für die Ereignisimplementierung als auch für den Aufruf schreiben. –

+0

_Wenn ein anderer Thread den Event-Handler nach dem if _ ... ok ... lösen wird. Wie also hat er es threadsicher gemacht, wenn nach dem _if statememnt_ ein anderer Thread die Aufrufliste gelöscht hat? –

5
if (SampleEvent != null) 
    SampleEvent(this, new MyEventArgs(val)); 

Dies ist ein klassisches Gewinde-Rennen. Ein anderer Thread könnte einen Ereignishandler abbestellen, während dieser Code ausgeführt wird. Dadurch wird die if() -Anweisung zu dem Schluss kommen, dass ein Subskribent, aber der Ereignisaufruf mit einer NullReferenceException fehlschlägt. Durch das Kopieren des Delegatobjekts in eine lokale Variable wird sichergestellt, dass der Clientcode, der den Delegatobjektverweis durch Abbestellen eines Ereignishandlers ändert, keinen Absturz verursacht. Immer noch ein Problem, Sie werden den Event-Handler nach dem Abmelden aufrufen, aber das ist ein unvermeidliches Rennen und nicht unbedingt fatal wie das NRE und kann im Gegensatz zum NRE vom Event-Handler erledigt werden.

Ja, eine Methode wie diese wird normalerweise als virtuell geschützt und OnSampleEvent() genannt, sodass eine abgeleitete Klasse das Ereigniserhöhungsverhalten ändern kann.

+0

NRE? Was ist es ? –

+1

Null Referenz Ausnahme :) – MBen

+0

_Kopieren in eine lokale Variable_: Ich bekomme es nicht, Delegate ist ein Referenztyp, wenn also eine andere Variable diesem Referenztyp entspricht, zeigen sie auf den gleichen Speicherort. Also was meinst du ? –