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


55

Sto cercando di capire cos'è un loop di eventi. Spesso la spiegazione è che in un ciclo di eventi, fai qualcosa fino a quando non ti viene comunicato che si è verificato un evento. Quindi gestisci l'evento e continui a fare quello che stavi facendo prima.

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


Tuttavia, questo evento sta accadendo e noi riceviamo una notifica "proprio così" per me è molto da gestire. Puoi dire: "Non è 'proprio così' devi registrare un listener di eventi". Ma cos'è un ascoltatore di eventi ma una funzione che per qualche motivo non ritorna. È nel suo 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, tuttavia solo un'astrazione. Credo che alla fine il polling sia inevitabile. Forse non lo stiamo facendo nel nostro codice, ma i livelli inferiori (l'implementazione del linguaggio di programmazione o il sistema operativo) lo stanno facendo per noi.

Fondamentalmente si riduce al seguente pseudo codice che è in esecuzione da qualche parte abbastanza in basso in modo da non provocare attese impegnate:

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

Questa è la mia comprensione dell'intera idea e vorrei sapere se questo è corretto. Sono aperto nell'accettare che l'intera idea sia fondamentalmente sbagliata, nel qual caso vorrei la spiegazione corretta.


3
I sistemi di eventi sono implementazioni del modello Observer . Comprendere il modello dovrebbe cementare la comprensione degli eventi. Non è richiesto il polling.
Steven Evers,

1
Alla fine sì, anche se i costrutti del linguaggio lo astraggono.
GrandmasterB,

@SteveEvers Nel tuo link wiki. Cosa sta EventSourcefacendo se non eseguire il polling dell'input da tastiera?
TheMeaningfulEngineer,

@Alan: Potrebbe fare qualsiasi cosa, ma per l'input da tastiera in particolare esistono API per registrare l'applicazione come ascolto di eventi da tastiera, che è possibile utilizzare come origine eventi senza polling. Ovviamente, se scendi abbastanza lontano, USB esegue sempre il polling, ma supponiamo che stiamo usando una tastiera PS / 2, che è guidata dall'interruzione, e quindi hai uno stack di input da tastiera basato su eventi con zero polling.
Phoshi,

Ho avuto queste domande da anni, ma non riesco a capire perché non mi sono mai preso la briga di farlo. Grazie, ora sono soddisfatto della comprensione con cui @Karl Bielefeldt mi ha illuminato.
0xc0de,

Risposte:


54

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

Supponiamo che l'evento sia 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 timer, movimenti del mouse, arrivo di un pacchetto, ecc.

In effetti, per la maggior parte dei sistemi operativi, il polling per 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. Si vede davvero solo il vero polling a livello hardware nei sistemi embedded, e anche lì non sempre.


4
Un interruzione non è un cambiamento di tensione su un filo? Questo può innescare un evento da solo o dobbiamo eseguire il polling del pin per il valore di tensione?
TheMeaningfulEngineer,

8
Ciò può innescare un evento da solo. Il processore è progettato in questo modo. In effetti un processore può essere svegliato dalla sospensione da un interrupt.
Karl Bielefeldt,

2
Sono confuso con l'affermazione Most event loops will block. Come si inserisce questo nel "paradigma del loop degli eventi, al contrario dell'uso dei thread, usa chiamate asincrone non bloccanti"?
TheMeaningfulEngineer,

1
Se guardi i loop di eventi per una libreria come GTK +, controllano la presenza di nuovi eventi, quindi chiamano i gestori di eventi in un ciclo, ma se non ci sono eventi, si bloccano su un semaforo o un timer o qualcosa del genere. I singoli sviluppatori realizzano i propri loop di eventi che non si bloccano in una coda di eventi vuota, ma le librerie ampiamente utilizzate si bloccano tutte. In caso contrario, i loop di eventi sono troppo inefficienti.
Karl Bielefeldt,

1
Suppongo che potresti guardarlo in quel modo, ma è multi thread nella libreria e / o nel sistema operativo piuttosto che nel codice dell'applicazione. I loop di eventi rimuovono i problemi di sincronizzazione per lo sviluppatore dell'applicazione.
Karl Bielefeldt,

13

Penso a un ascoltatore di eventi non come una funzione che esegue il proprio loop, ma come una staffetta con il primo corridore in attesa della pistola di partenza. Un motivo significativo per l'utilizzo degli eventi invece del polling è che sono più efficienti con i cicli della CPU. Perché? Guardalo dall'hardware in alto (piuttosto che dal codice sorgente in basso).

Prendi in considerazione un server Web. Quando il tuo server chiama listen()e si blocca, il tuo codice sta prendendo il suo posto come relay relay. Quando arriva il primo pacchetto di una nuova connessione, la scheda di rete inizia la gara interrompendo il sistema operativo. Il sistema operativo esegue una routine di servizio di interruzione (ISR) che acquisisce 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 quanto ne sappiamo, tra le gare ogni corridore a staffetta potrebbe andare al pub. Un punto di forza dell'astrazione dell'evento è che il codice non deve conoscere o preoccuparsi.

Alcuni sistemi operativi includono il codice di gestione degli eventi che esegue la sua parte della gara, passa il testimone e poi ritorna al punto di partenza per attendere l'inizio della gara successiva. In tal senso, la gestione degli eventi è il polling ottimizzato in molti loop simultanei. Tuttavia, esiste sempre un trigger esterno che avvia il processo. Il listener di eventi non è una funzione che non sta tornando, ma una funzione che sta aspettando quel trigger esterno prima che venga eseguito. 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 la signale la prossima volta che il gestore viene eseguito, non c'è concettualmente nessun codice in esecuzione o in loop.


Questo ha senso, tuttavia cosa sta facendo il loop degli eventi mentre è in attesa dell'interruzione? Se sta bloccando, non è l'approccio "multi threaded" che è il paradigma opposto al loop degli eventi?
TheMeaningfulEngineer,

Se il tuo codice ha il loop, forever: { select(); do stuff; }stai rientrando in gara ogni volta attraverso il loop. Sia che lo facciate ripetutamente da un singolo thread o in parallelo su thread o processori separati, penso a ciascun evento come alla sua razza. Ad esempio, un browser Web è un programma multithread con più loop di eventi in thread separati, almeno uno per l'interfaccia utente e uno per ogni pagina che sta scaricando. La domanda che faccio quando si codifica è "come posso elaborare gli eventi abbastanza velocemente?" A volte la risposta è un loop, a volte thread, spesso una combinazione.
cxw,

Ho programmato per eventi per più di un paio di decenni e ho trovato questa risposta molto confusa. Affinché un ascoltatore funzioni, ci deve essere un ciclo che attende un evento, quindi lo instrada a tutti i listener registrati. (Supponendo che stiamo parlando di software piuttosto che di interruzioni hardware)
Bryan Oakley,

8

No. Non si tratta di "polling ottimizzato". Un loop di eventi utilizza I / O guidato da interrupt anziché polling.

Mentre i loop Until, For, ecc. Sono loop polling.

"Polling" è il processo di controllo ripetuto di qualcosa. Poiché il codice del ciclo viene eseguito continuamente e poiché si tratta di un ciclo "stretto", il processore ha poco tempo per passare da un compito all'altro e fare qualsiasi altra cosa. Quasi tutti i "blocchi", i "blocchi", i "blocchi" o qualunque cosa tu voglia chiamarlo 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 viene fatto ogni sforzo per eliminarlo o minimizzarlo.

Tuttavia, per ottimizzare la qualità del codice, la maggior parte delle lingue tenta di utilizzare il paradigma del ciclo di polling il più vicino possibile per i comandi di gestione degli eventi poiché servono a scopi funzionalmente simili all'interno di un programma. Pertanto, poiché il polling è il modo più familiare di attendere la pressione di un tasto o qualcosa del genere, è facile per gli inesperti 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 guidata dall'interruzione, nella CPU viene impostato essenzialmente un "flag" e il processo viene "sospeso" (non consentito l'esecuzione) fino a quando tale flag non viene modificato da un altro processo (come la tastiera autista che lo cambia quando l'utente ha premuto un tasto). Se il flag è una condizione hardware effettiva come una linea "alta", viene chiamata "interruzione" o "interruzione hardware". La maggior parte, tuttavia, sono implementati come solo un 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 veloce e semplice tra i processi software.

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

Ho lasciato molto fuori ma ho dovuto fermarmi da qualche parte. Si prega di chiedere se avete bisogno di maggiori dettagli.


7

In genere la risposta è hardware, il sistema operativo e i thread in background che non controlli cospirano per renderlo semplice. La scheda di rete riceve alcuni dati che genera un interrupt per informare la CPU. Il gestore di interrupt del sistema operativo lo gestisce. Quindi un thread in background che non controlli (che è stato creato registrandosi per l'evento e è rimasto inattivo da quando ti sei registrato per l'evento) viene attivato dal sistema operativo come parte della gestione dell'evento ed esegue il gestore dell'evento.


7

Ho intenzione di andare contro tutte le altre risposte che vedo finora e dire "sì" . Penso che le altre risposte stiano complicando 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 di eventi per la prima volta, pensarli come un semplice loop non farà alcun male. Alcuni framework sottostanti stanno aspettando che il sistema operativo fornisca un evento, quindi instrada l'evento a uno o più gestori, quindi attende l'evento successivo e così via. Questo è davvero tutto ciò che c'è da un punto di vista del software applicativo.


1
+1 per essere semplice. Ma l'enfasi è sull'attesa; l'esecuzione di questo codice si interromperà sulla prima riga del ciclo e non continuerà fino a quando si verifica un evento. Ciò è diverso da un ciclo di polling occupato che verifica ripetutamente la presenza di un evento fino a quando non si verifica.
Tom Panning,

1

Non tutti i trigger di eventi sono gestiti in loop. Il modo in cui scrivo spesso i miei motori di eventi sarebbe così:

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
}

Nota che sebbene il Memo 1 sia su un loop, il loop serve per avvisare ogni ascoltatore. Il trigger di evento stesso non è necessariamente in un ciclo.

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.