2009-10-26 20 views
5

Sono davvero imbarazzato nel fare una domanda così banale, ma il debug di alcuni software ora mi ha convinto di non capire veramente questo problema:Asincronia e eventi .NET?

Come funzionano gli eventi .NET da un'altitudine di 20.000 piedi? Non intendo il modello delegato/gestore di eventi e tutto questo. Quello che voglio dire è - qual è la foto GRANDE:

  1. Il codice A sta facendo qualcosa.
  2. Si verifica un trigger esterno. Supponiamo, ad esempio, che l'utente abbia fatto clic su un controllo.
  3. Una magia accade e il gestore di eventi per l'evento viene chiamato.
  4. un'altra magia accade dopo il gestore eventi restituisce.

Ora, qual è la magia? In che modo questo si riferisce ai thread? Il thread che esegue il mio codice viene interrotto quando si verifica l'evento, quindi riprende dopo il ritorno del gestore eventi? Ma ho cercato su google e found out che i gestori .NET vengano chiamati in modo sincrono nel thread originale. Quindi chi si occupa di fermare e riprendere il codice A? Cosa succede se gli eventi sono nidificati (ad esempio, l'evento 2 si verifica quando è in esecuzione il gestore eventi per l'evento 1)?

Edit: Per quanto ho capito le risposte dicono che la curva evento per il prossimo evento verrà eseguito solo dopo che il gestore di eventi attualmente in esecuzione termina. Ciò significa che il tuo codice non viene interrotto: la linea n verrà sempre eseguita immediatamente subito dopo la riga n-1 e immediatamente prima della riga n + 1. Tuttavia poco prima che ho postato la domanda che mi è stato il debug di un programma di controllo, attraverso l'automazione, Internet Explorer (utilizzando SWExplorerAutomation da Webius). Sono abbastanza sicuro che, come ero line-passo attraverso il codice mi è stato "rapito" :-) a qualche gestore di eventi e tornato alla posizione di interruzione nel codice una volta che gestore di eventi ha terminato la sua attività. Questo significa che o non capisci le risposte, o che il programma si comporta in modo diverso mentre sei passato attraverso il debugger!

risposta

3

Permettetemi di far luce sul vostro problema. La magia è il ciclo dei messaggi di Windows. Vedete nel vostro esempio, in realtà, non c'è nulla che impedisca al codice A di fermarsi quando si verifica un evento. Piuttosto, questa è la sequenza.

Quando il codice A è in esecuzione, l'utente fa clic su un pulsante. Il messaggio della finestra del pulsante viene messo in coda, ma non accade nulla. Quando il codice Un esce la sua funzione o cede il controllo al ciclo di messaggi, allora l'evento Click viene elaborato e il gestore eventi viene eseguito.

Prova questo esperimento. Metti un ciclo infinito all'interno del tuo programma sul thread principale e poi fai clic sull'interfaccia utente. Si noterà che l'interfaccia utente non risponderà e non ci saranno Event Handlers in esecuzione.

+0

Questo vale per le applicazioni WinForms, ma non dimenticare il caso più generale, in cui non è coinvolta alcuna pompa di messaggio. In questo caso, tutto viene eseguito in modo sincrono senza un mitigatore e non è implicata alcuna magia. – bzlm

+0

@bzim Capisco il buon vecchio mulit-mailboxes, wake-me-up-when-a-message-arriva-if-I'm-with-a-higher-priority-than-the-correntemente-running-task model come nel buon vecchio RMX di Intel. Quello che non capisco è il gioco di prestigio di Windows. – Avi

2

La cosa che vedrà da 20.000 piedi è il MessageLoop. È all'interno di Application.Run().

In poche parole, questo è un ciclo while che gestisce l'intero ciclo di vita della vostra applicazione e fa

// pseudo code, I did not reflector Application.Run 
    while (GetMessage(ref msg) 
    { 
    DispatchMessage(ref msg); 
    } 

Si noterà la simgle fili quando si prende troppo a lungo la manipolazione 1 evento, la vostra applicazione sarà etichettato 'non responsivo' in TaskManager.

Un metodo correlato è Application.DoEvents(), ma stare lontano da quello.

+0

Questo vale per le applicazioni WinForms, ma non dimenticare il caso più generale, in cui non è coinvolta alcuna pompa di messaggio. In questo caso, tutto viene eseguito in modo sincrono senza un mitigatore e non è implicata alcuna magia. – bzlm

+0

bzim, ho reagito all'esempio di Control-clic. Hai ragione in generale, ma penso che l'OP stia pensando a WinForms. –

1

Gli eventi sono puntatori alle funzioni (proprio come in C++). Quando si utilizza l'evento .NET di plain vanilla, si stanno effettivamente chiamando le funzioni connesse usando + = a quell'evento. Quindi da 20.000 piedi il tuo codice chiama effettivamente un altro codice, proprio come chiamare un'altra funzione. Questo è il motivo per cui è chiamato in modo sincrono e nella stessa discussione.

Quando all'interno del controllo WinForms/WPF viene anche preso in considerazione un ciclo di messaggi: Tutti gli eventi che si verificano nel contesto del modulo aggiungono un messaggio al ciclo di messaggi anziché chiamare direttamente un metodo.

Il thread principale del controllo esegue il polling di quel loop per i nuovi messaggi e quando viene visualizzato un nuovo messaggio lo esegue (di nuovo nel thread principale) solo ora non è esattamente sincrono.

Questo è il motivo per cui se un modulo è occupato a fare qualcosa e si preme un pulsante, ci vuole del tempo prima che venga premuto quel pulsante. questo è anche il motivo per cui se invalidi un controllo il suo aspetto viene modificato solo dopo aver abbandonato il metodo in esecuzione (e il successivo messaggio viene elaborato).

+0

0. Grazie per la risposta :-) 1. Stai dicendo che OGNI controllo (pulsante, etichetta, elenco, ecc.) Su un modulo ha il proprio ciclo di messaggi e il suo thread? E ogni controllo HTML all'interno di una pagina Web? Questo sembra davvero uno spreco. 2. Non ho capito l'intera sezione che inizia con "Questa è la ragione ...". Potresti spiegare? – Avi

+0

1. Se ricordo correttamente Ogni finestra principale ha il proprio loop di messaggi - non ogni controllo 2. Il loop di messaggi è la ragione per cui le chiamate nello stesso thread non sono sincrone - cioè premendo un pulsante non si provoca l'evento di pressione del pulsante sparare immediatamente, ma funziona nel contesto del thread principale. –

+0

Dror, non tutte le finestre, tutte le applicazioni. E mentre puoi iniziare un secondo che è molto raro. –