Un ciclo di eventi è solo un ciclo for / while con polling ottimizzato?

54

Sto cercando di capire cos'è un loop di eventi. Spesso la spiegazione è che in un ciclo di eventi, fai qualcosa finché non ti viene notificato che si è verificato un evento. Quindi gestisci l'evento e continua a fare ciò che stavi facendo in precedenza.

Per mappare la definizione di cui sopra con un esempio. Ho un server che 'ascolta' in un ciclo di eventi, e quando viene rilevata una connessione socket, i dati da esso vengono letti e visualizzati, dopo di che il server riprende / inizia ad ascoltare come faceva prima.

Tuttavia, questo evento sta accadendo e noi che riceviamo la notifica "proprio così" sono molto per me da gestire. Puoi dire: "Non è" così "devi registrare un listener di eventi". Ma cos'è un listener di eventi ma una funzione che per qualche motivo non sta tornando. È nel proprio ciclo, in attesa di essere avvisato quando si verifica un evento? Il listener di eventi dovrebbe anche registrare un listener di eventi? Dove finisce?

Gli eventi sono una bella astrazione con cui lavorare, ma solo un'astrazione. Credo che alla fine il sondaggio sia inevitabile. Forse non lo stiamo facendo nel nostro codice, ma i livelli più bassi (l'implementazione del linguaggio di programmazione o il sistema operativo) lo stanno facendo per noi.

In pratica si tratta del seguente pseudo-codice che sta girando da qualche parte abbastanza basso da non risultare in attesa:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Questa è la mia comprensione dell'intera idea e mi piacerebbe sentire se è corretta. Sono aperto ad accettare che l'intera idea sia fondamentalmente sbagliata, nel qual caso vorrei la spiegazione corretta.

    
posta TheMeaningfulEngineer 18.10.2013 - 22:03
fonte

6 risposte

52

La maggior parte dei loop di eventi sospenderà se non ci sono eventi pronti, il che significa che il sistema operativo non darà all'attività alcun tempo di esecuzione fino a quando non si verifica un evento.

Dire che l'evento è un tasto premuto. Potresti chiedere se c'è un ciclo da qualche parte nel sistema operativo che controlla la pressione dei tasti. La risposta è no. I tasti premuti generano un interrupt , che viene gestito in modo asincrono dall'hardware. Allo stesso modo per i timer, i movimenti del mouse, un pacchetto in arrivo, ecc.

In effetti, per la maggior parte dei sistemi operativi, il polling per gli eventi è l'astrazione. L'hardware e il sistema operativo gestiscono gli eventi in modo asincrono e li inseriscono in una coda che può essere interrogata dalle applicazioni. Vedete solo veri sondaggi a livello hardware nei sistemi embedded, e anche lì non sempre.

    
risposta data 18.10.2013 - 22:42
fonte
13

Penso ad un listener di eventi non come una funzione che esegue il proprio loop, ma come una staffetta con il primo corridore che aspetta la pistola di partenza. Un motivo significativo per utilizzare gli eventi anziché il polling è che sono più efficienti con i cicli della CPU. Perché? Guardalo dall'hardware in su (piuttosto che il codice sorgente in basso).

Considera un server Web. Quando il tuo server chiama listen() e blocca, il tuo codice sta prendendo il posto di un relay runner. Quando arriva il primo pacchetto di una nuova connessione, la scheda di rete avvia la gara interrompendo il sistema operativo. Il sistema operativo esegue una routine di servizio di interrupt (ISR) che cattura il pacchetto. L'ISR passa il testimone a una routine di livello superiore che stabilisce la connessione. Una volta che la connessione è attiva, quella routine passa il testimone a listen() , che passa il testimone al tuo codice. A quel punto, puoi fare quello che vuoi con la connessione. Per quel che ne sappiamo, tra una corsa e l'altra ogni corridore di staff potrebbe andare al pub. Una forza dell'astrazione dell'evento è che il tuo codice non deve sapere o preoccuparsi.

Alcuni sistemi operativi includono il codice di gestione degli eventi che esegue la sua porzione di gara, mani fuori dal testimone, quindi torna al punto di partenza per attendere l'inizio della gara successiva. In questo senso, la gestione degli eventi è ottimizzata per il polling in molti cicli concomitanti. Tuttavia, c'è sempre un trigger esterno che dà il via al processo. Il listener di eventi non è una funzione che non viene restituita, ma una funzione che attende il trigger esterno prima dell'esecuzione. Piuttosto che:

while(True):
    do stuff
    check if event has happened (poll)
    do other stuff

Penso a questo come:

on(some event):    //I got the baton
     do stuff
     signal the next level up    //Pass the baton

e tra signal e la volta successiva che il gestore viene eseguito, non vi è concettualmente alcun codice in esecuzione o in loop.

    
risposta data 18.10.2013 - 22:43
fonte
8

No. Non è "polling ottimizzato". Un ciclo di eventi utilizza I / O interrotto invece del polling.

I cicli While, Until, For, ecc. sono cicli di polling.

"Polling" è il processo di controllo ripetuto di qualcosa. Poiché il codice di loop viene eseguito continuamente, e perché è un ciclo piccolo, "stretto", il processore impiega poco tempo per cambiare attività e fare qualsiasi altra cosa. Quasi tutti "si blocca", "si blocca", "blocchi" o qualsiasi altra cosa si voglia chiamare quando il computer non risponde, sono la manifestazione del codice bloccato in un ciclo di polling non intenzionale. La strumentazione mostrerà il 100% di utilizzo della CPU.

I loop di eventi guidati da interrupt sono molto più efficienti dei loop di polling. Il polling è un uso estremamente dispendioso dei cicli della CPU, quindi è stato fatto ogni sforzo per eliminarlo o ridurlo al minimo.

Tuttavia, per ottimizzare la qualità del codice, la maggior parte delle lingue tenta di utilizzare il paradigma del ciclo di polling il più fedelmente possibile per i comandi di gestione degli eventi poiché servono a scopi analoghi a livello funzionale all'interno di un programma. Quindi, essendo il sondaggio il modo più familiare per aspettare un keypress o qualcosa del genere, è facile per l'inesperto usarlo e finire con un programma che può funzionare bene da solo, ma nient'altro funziona mentre è in esecuzione. Ha "preso il controllo" della macchina.

Come spiegato in altre risposte, nella gestione degli eventi basata su interrupt, essenzialmente un "flag" è impostato all'interno della CPU e il processo è "sospeso" (non è permesso l'esecuzione) finché tale flag non viene modificato da qualche altro processo (come come il driver della tastiera che lo cambia quando l'utente ha premuto un tasto). Se il flag è una condizione hardware reale come una linea "alta", viene chiamata "interrupt" o "interrupt hardware". La maggior parte, tuttavia, sono implementati come un semplice indirizzo di memoria nella CPU o nella memoria principale (RAM) e sono chiamati "semafori".

I semafori possono essere modificati sotto il controllo del software e quindi possono fornire un meccanismo di segnalazione molto rapido e semplice tra i processi software.

Gli interrupt, tuttavia, possono essere modificati solo dall'hardware. L'uso più frequente degli interrupt è quello attivato a intervalli regolari dal chip dell'orologio interno. Uno degli innumerevoli tipi di azioni software attivate dagli interrupt di clock, è il cambio di semafori.

Ho omesso molto ma ho dovuto fermarmi da qualche parte. Si prega di chiedere se avete bisogno di ulteriori dettagli.

    
risposta data 25.10.2013 - 07:54
fonte
7

In genere la risposta è hardware, il sistema operativo e i thread in background non controllati cospirano per renderlo più semplice. La scheda di rete riceve alcuni dati alza un interrupt per far sapere alla CPU. Il gestore di interrupt del sistema operativo lo gestisce. Quindi un thread in background non controllato (che è stato creato registrandosi per l'evento e ha dormito da quando ti sei registrato per l'evento) viene riattivato dal sistema operativo come parte della gestione dell'evento e viene eseguito il gestore di eventi.

    
risposta data 18.10.2013 - 22:43
fonte
7

Ho intenzione di andare contro tutte le altre risposte che vedo finora e di dire "sì" . Penso che le altre risposte complichino troppo le cose. Da un punto di vista concettuale, tutti i loop di eventi sono essenzialmente:

while <the_program_is_running> {
    event=wait_for_next_event()
    process_event(event)
}

Se stai cercando di capire i loop degli eventi per la prima volta, pensare a loro come a un ciclo semplice non danneggerà. Alcuni framework sottostanti sono in attesa che il sistema operativo invii un evento, quindi indirizza l'evento a uno o più gestori, quindi attende il prossimo evento e così via. Questo è tutto ciò che c'è da fare da una prospettiva di software applicativo.

    
risposta data 19.10.2013 - 01:25
fonte
1

Non tutti gli eventi trigger sono gestiti in loop. Il modo in cui scrivo spesso i miei motori di eventi è simile a questo:

interface Listener {
    void handle (EventInfo info);
}

List<Listener> registeredListeners

void triggerEvent (EventInfo info) {
    foreach (listener in registeredListeners) { // Memo 1
        listener.handle(info) // the handling may or may not be synchronous... your choice
    }
}

void somethingThatTriggersAnEvent () {
    blah
    blah
    blah
    triggerEvent(someGeneratedEventInfo)
    more blah
}

Si noti che sebbene Memo 1 si trovi su un ciclo, il ciclo serve a notificare ogni ascoltatore. L'evento trigger stesso non è necessariamente in un ciclo.

In teoria, gli eventi chiave a livello di OS possono utilizzare la stessa tecnica (anche se penso che spesso eseguano il polling? Sto solo speculando qui), a condizione che il sistema operativo esponga una specie di registerListener API.

    
risposta data 18.10.2013 - 22:15
fonte