2009-06-24 21 views
5

L'implementazione è sotto thread-safe? Se no, cosa mi sto perdendo? Devo avere le parole chiave volatile da qualche parte? O un blocco da qualche parte nel metodo OnProcessingCompleted? Se sì, dove?C#: eventi thread-safe

public abstract class ProcessBase : IProcess 
{ 
    private readonly object completedEventLock = new object(); 

    private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; 

    event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted 
    { 
     add 
     { 
      lock (completedEventLock) 
       ProcessCompleted += value; 
     } 
     remove 
     { 
      lock (completedEventLock) 
       ProcessCompleted -= value; 
     } 
    } 

    protected void OnProcessingCompleted(ProcessCompletedEventArgs e) 
    { 
     EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted; 
     if (handler != null) 
      handler(this, e); 
    } 
} 

Nota: Il motivo per cui ho evento privato e roba interfaccia esplicita, è perché è una classe base astratta. E le classi che ne ereditano non dovrebbero fare nulla direttamente con quell'evento. Aggiunta l'involucro di classe in modo che sia più chiaro =)

+0

(risposto al commento) –

risposta

4

Non v'è alcuna necessità per il ProcessCompleted membro privato di essere un event - potrebbe essere solo un campo: - all'interno della classe si va sempre dritto al campo , quindi la roba event viene persa comunque.

L'approccio hai mostrato con un oggetto di blocco esplicito non è molto più thread-safe che avere un evento di campo simile (cioè public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - l'unica differenza è che non si blocco "questo" (che è una buona cosa - si dovrebbe idealmente evitare di bloccare il this). .. l'approccio "variabile handler" è quella giusta, ma ci sono ancora side-effects you should be aware of

+0

Aggiunto perché ho utilizzato l'eventhandler privato e gli eventi espliciti per la mia domanda. Non è ancora necessario? E cosa intendi con questa differenza? Un evento pubblico EventHandler SomeEvent si blocca automaticamente su questo? – Svish

+2

Sì; eventi di tipo campo (ad esempio un evento senza un add/remove esplicito) ha un blocco integrato (questo); vedi 10.8.1 nelle specifiche del linguaggio (versione MS); tuttavia, questo è aggirato dal codice all'interno della classe - vedi http://marcgravell.blogspot.com/2009/02/fun-with-field-like-events.html; così come un evento * private *, l'add/remove (e quindi lock) non viene mai usato. Per un'implementazione esplicita dell'interfaccia, il codice va bene e dovrai aggiungere tu stesso il blocco, cosa che hai fatto tu - e probabilmente * meglio * di un "blocco (questo)". Rimanere con esso ;-p –

+0

alrighty =) – Svish

5

è necessario bloccare quando si recupera il gestore troppo , altrimenti potresti non avere l'ultimo valore:

protected void OnProcessingCompleted(ProcessCompletedEventArgs e) 
{ 
    EventHandler<ProcessCompletedEventArgs> handler; 
    lock (completedEventLock) 
    { 
     handler = ProcessCompleted; 
    } 
    if (handler != null) 
     handler(this, e); 
} 

Nota non che questo non prevenire una condizione di competizione in cui abbiamo deciso che andremo ad eseguire una serie di gestori e poi un gestore sottoscritte. Sarà ancora chiamato, perché abbiamo recuperato il delegato multicast che lo contiene nella variabile handler.

Non c'è molto che si può fare a questo proposito, oltre a rendere consapevole al gestore stesso che non dovrebbe essere più chiamato.

E 'senza dubbio meglio proprio non cercare per rendere gli eventi thread-safe - precisare che la sottoscrizione dovrebbe solo cambiamento nel filo che generare l'evento.

+0

Sei sicuro che il blocco è necessario? I delegati sono immutabili e l'assegnazione è un'operazione atomica, quindi nessuna serratura è necessaria, penso. – TcKs

+0

Vedi i miei commenti al tuo post. È assolutamente necessario il blocco per renderlo thread-safe. –

+0

Sì, i blocchi in "Aggiungi" e "Rimuovi" sono necessari. Ma quale beneficio ho ottenuto usando "lock" in "OnProcessingCompleted"? – TcKs