2010-04-06 17 views
27

Così ho letto in giro che invece di chiamare un evento direttamente conI gestori di eventi non sono thread safe?

if (SomeEvent != null) 
    SomeEvent(this, null); 

dovrei fare

SomeEventHandler temp = SomeEvent; 
if (temp != null) 
    temp(this, null); 

Perché è così? In che modo la seconda versione diventa sicura? Qual è la migliore pratica?

+0

Leggendo le risposte provvisorie e qualificate qui ho la sensazione che la gestione degli eventi in C# sia strettamente accoppiata, soggetta a errori e non compresa molto bene. – micahhoover

risposta

13

Gli eventi sono davvero zucchero sintattico su un elenco di delegati. Quando invochi l'evento, questo sta davvero iterando su quell'elenco e invoca ciascun delegato con i parametri che hai passato.

Il problema con i thread è che potrebbero aggiungere o rimuovere elementi da questa raccolta sottoscrivendo/annullando l'iscrizione. Se lo fanno mentre stai iterando la raccolta ciò causerà problemi (penso che venga generata un'eccezione)

L'intento è quello di copiare l'elenco prima di iterarlo, in modo da essere protetto dalle modifiche alla lista.

Nota: Ora è possibile invocare l'ascoltatore anche dopo aver annullato l'iscrizione, pertanto è necessario accertarsi di gestirlo nel codice del listener.

+2

In realtà, non causerà un problema mentre si itera sulla collezione. I delegati in gioco qui sono immutabili. L'unico problema è la frazione di secondo prima di verificare se qualcuno è iscritto e in realtà chiama i gestori di eventi. Una volta che hai iniziato a eseguirli, nessuna modifica in background influirà sul richiamo corrente. –

5

Le migliori pratiche sono la seconda forma. Il motivo è che un altro thread potrebbe annullare o modificare SomeEvent tra il test "if" e il richiamo.

+0

Perché non può succedere nella seconda affermazione? – Daniel

+2

La lettura è di 'SomeEvent' è atomica, cioè accade tutto o niente. Quindi 'temp' non può essere modificato al di fuori di quel thread a causa di essere un locale. – user7116

+0

Ooops 's/Thus/Also /' – user7116

2

Here è una buona scrittura su eventi .NET e condizioni di gara con thread. Copre alcuni scenari comuni e contiene alcuni buoni riferimenti.

Spero che questo aiuti.

+0

Grazie per il collegamento – Daniel

29

IMO, le altre risposte mancano un dettaglio chiave - che delegati (e quindi eventi) sono immutabili. L'importanza di ciò è che l'iscrizione o l'annullamento dell'iscrizione di un gestore di eventi non è semplicemente aggiungi/rimuovi a un elenco, ma sostituisce sostituisce l'elenco con uno nuovo con un elemento aggiuntivo (o uno in meno).

Dal riferimenti sono atomiche, questo significa che al punto che fai:

var handler = SomeEvent; 

ora avete una rigida un'istanza che non può cambiare, anche se nella prossima picosecondo un altro unsubscribes filo (causando la campo effettivo per diventare null).

Così testate per null e invocatelo, e tutto va bene. Si noti che c'è ancora lo scenario confuso dell'evento è sollevato su un oggetto che lo ritiene non sottoscritto un picosecondo fa!

+0

Ciò riguarda una preoccupazione, ma la sostituzione stessa viene eseguita in modo sicuro in modo tale da garantire che tutte le operazioni richieste avvengano? Ad esempio, se due thread tentano di sottoscrivere delegati distinti allo stesso tempo, è garantito che entrambi saranno sottoscritti alla fine o è possibile che uno di questi abbonamenti fallisca in modo silenzioso? – binki

+1

@binki sì; usa un ciclo di scambio interbloccato (compilatore moderno) o una regione sincronizzata ('lock') (compilatori più vecchi) –