2012-07-16 12 views
11

Utilizzando miei EventArgs Cusom come ad esempio:EventHandler <TEventArgs> sicurezza thread in C#?

public event EventHandler<MyEventArgs> SampleEvent; 

da msdn esempio:

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)); 
    } 
} 

ho domanda:

1) guardando il codice marcato:

enter image description here

non vedo un motivo per cui dovrebbe essere copiato su un altro parametro (per quanto riguarda le discussioni)

dal momento che abbiamo la event keyowrd, nessuno può toccare il suo elenco invocazione (nessun codice estraneo alla classe intendo)

2) Se im non sbaglio, la funzione DemoEvent dovrebbe essere virtuale, in modo che possa essere sovrascritto in classi di sub ... (im sicuro Ho visto da qualche parte)

la cosa strana è che ReSharper anche wont aggiungere virtuale :

Quindi, se ho questo codice:

enter image description here

mi suggerisce:

enter image description here

e quando ho premerlo:

enter image description here

così ancora una volta la mia domande:

1) Qual è lo scenario che risolverà questa riga EventHandler<MyEventArgs> temp = SampleEvent; in merito alla sicurezza dei thread?

2) la funzione non deve essere virtual? (sono sicuro che abbia visto questo modello con virtuale)

+0

Il riassuntore è solo un punto di vista su giusto e sbagliato. Non è un assoluto – podiluska

+1

Nota che ci sono in realtà due condizioni di gara qui. Questo cambiamento di codice corregge solo una di queste razze. Vedi l'eccellente articolo di Eric Lippert, [Eventi e Razze] (http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx) per una spiegazione completa. – Brian

+1

possibile duplicato di [Eventi C# e sicurezza thread] (http://stackoverflow.com/questions/786383/c-sharp-events-and-thread-safety) –

risposta

10

qual è lo scenario che questa riga EventHandler temp = SampleEvent; risolverà, per quanto riguarda la sicurezza dei thread?

Immaginate di fare questo:

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

Se un altro thread si staccherà il gestore di eventi dopo il se, ma prima l'invocazione Allora ti tenta di chiamare un delegato null (e si otterrà un eccezione).

non dovrebbe la funzione essere virtuale?(Im sicuro Ho visto questo modello con il virtuale)

Sì, se la classe non è sealed allora si dovrebbe segnare che la funzione virtual (non è obbligatorio, ma è un modello ben accetto).

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 questo caso al momento 4 si chiama un delegato null e ti butti un NullReferenceException. Ora guardate questo codice:

 
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  } 

Ora in fase 5 si chiama sampleEvent e che detiene il contenuto vecchia di SampleEvent, in questo caso non getterà alcuna eccezione (ma chiamerai MyHandler anche se è stato rimosso dal secondo thread).

+0

Immagino che ciò significhi che alcuni abbonati potrebbero ancora ricevere un evento evento innescando anche dopo che erano stati staccati. – apokryfos

+2

@apokryfos si, è vero. Una soluzione migliore dovrebbe comportare l'implementazione di un evento personalizzato, ma anche in questo caso si potrebbe ottenere un comportamento indesiderato con thread e blocchi. Non esiste una soluzione buona e generale (AFAIK), probabilmente se devi evitare questi problemi devi scrivere un codice personalizzato lungo/lento per l'implementazione e l'invocazione dell'evento. –

+0

_Se un altro thread scollega il gestore eventi dopo il if _ ... ok .... quindi come è stato reso thread-safe se dopo il _if statememnt_ un altro thread ha cancellato l'elenco di chiamate? –

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

Questa è una corsa di threading classica. Un altro thread potrebbe annullare l'iscrizione a un gestore di eventi durante l'esecuzione di questo codice. Questo fa sì che l'affermazione if() concluda che c'è un abbonato, ma la chiamata all'evento fallisce con una NullReferenceException. La copia dell'oggetto delegato in una variabile locale garantisce che il codice client che modifica il riferimento all'oggetto delegato annullando l'iscrizione di un gestore eventi non causi un arresto anomalo. Ancora un problema, chiamerai il gestore di eventi dopo che è stato annullato, ma questa è una gara inevitabile e non necessariamente fatale come la NRE e può essere gestita dal gestore di eventi, a differenza della NRE.

Sì, un metodo come questo viene solitamente reso protetto e denominato OnSampleEvent() in modo che una classe derivata possa alterare il comportamento dell'evento.

+0

NRE? che cos'è ? –

+1

Eccezione riferimento null :) – MBen

+0

_Copiare in una variabile locale_: Non lo ottengo, delegato è un tipo di riferimento, quindi se un'altra variabile è uguale a questo tipo di riferimento, punta alla stessa posizione di memoria. quindi cosa vuoi dire ? –