¿Es un bucle de eventos solo un bucle for / while con sondeo optimizado?

55

Estoy tratando de entender qué es un ciclo de eventos. A menudo, la explicación es que en un bucle de eventos, usted hace algo hasta que se le notifique que ha ocurrido un evento. Luego manejas el evento y continúas haciendo lo que estabas haciendo antes.

Para mapear la definición anterior con un ejemplo. Tengo un servidor que 'escucha' en un bucle de eventos, y cuando se detecta una conexión de socket, los datos se leen y se muestran, después de lo cual el servidor se reanuda / comienza a escuchar como lo hizo antes.


Sin embargo, este evento que está ocurriendo y que nos notifiquen 'así como así' es demasiado para mí. Puede decir: "No es" así ", debe registrar un oyente de eventos". Pero, ¿qué es un detector de eventos sino una función que por alguna razón no regresa? ¿Está en su propio ciclo, esperando ser notificado cuando ocurre un evento? ¿El oyente de eventos también debe registrar un oyente de eventos? Donde termina


Los eventos son una buena abstracción para trabajar, sin embargo, solo una abstracción. Creo que al final, las encuestas son inevitables. Quizás no lo estamos haciendo en nuestro código, pero los niveles inferiores (la implementación del lenguaje de programación o el sistema operativo) lo están haciendo por nosotros.

Básicamente se reduce al siguiente pseudocódigo que se está ejecutando en un lugar lo suficientemente bajo como para no generar una espera ocupada:

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

Esta es mi comprensión de toda la idea, y me gustaría saber si esto es correcto. Estoy abierto a aceptar que toda la idea es fundamentalmente errónea, en cuyo caso me gustaría la explicación correcta.

El Ingeniero Significante
fuente
3
Los sistemas de eventos son implementaciones del patrón Observador . Comprender el patrón debería cimentar su comprensión de los eventos. No se requiere sondeo.
Steven Evers
1
En definitiva sí, incluso si las construcciones del lenguaje lo abstraen.
GrandmasterB
@SteveEvers En tu enlace wiki. ¿Qué está EventSourcehaciendo si no sondea la entrada del teclado?
TheMeaningfulEngineer
@ Alan: Podría estar haciendo cualquier cosa, pero para la entrada de teclado específicamente existen API para registrar su aplicación como escucha de eventos de teclado, que luego puede usar como su fuente de eventos sin necesidad de encuestas. Por supuesto, si baja lo suficiente, el USB siempre está sondeando, pero supongamos que estamos usando un teclado PS / 2, que es controlado por interrupción, y luego tiene una pila de entrada de teclado basada en eventos con sondeo cero.
Phoshi
He tenido estas preguntas desde hace años, pero no puedo decir por qué nunca me molesté en preguntar. Gracias, ahora estoy satisfecho con la comprensión con la que @Karl Bielefeldt me ha iluminado.
0xc0de

Respuestas:

54

La mayoría de los bucles de eventos se suspenderán si no hay eventos listos, lo que significa que el sistema operativo no dará tiempo de ejecución a la tarea hasta que ocurra un evento.

Digamos que el evento es una tecla que se está presionando. Puede preguntar si hay un bucle en alguna parte del sistema operativo que comprueba si se presionan teclas. La respuesta es no. Las teclas que se presionan generan una interrupción , que el hardware maneja de forma asíncrona. Del mismo modo para temporizadores, movimientos del mouse, un paquete que llega, etc.

De hecho, para la mayoría de los sistemas operativos, el sondeo de eventos es la abstracción. El hardware y el sistema operativo manejan los eventos de forma asíncrona y los colocan en una cola que las aplicaciones pueden sondear. Solo se ven verdaderas encuestas a nivel de hardware en sistemas integrados, e incluso allí no siempre.

Karl Bielefeldt
fuente
44
¿No es una interrupción un cambio de voltaje en un cable? ¿Puede eso desencadenar un evento por sí mismo o debemos sondear el pin para el valor del voltaje?
TheMeaningfulEngineer
8
Eso puede desencadenar un evento por sí mismo. El procesador está diseñado de esa manera. De hecho, una interrupción puede despertar un procesador del modo inactivo.
Karl Bielefeldt
2
Estoy confundido con la declaración Most event loops will block. ¿Cómo encaja esto en "el paradigma del bucle de eventos, en lugar de usar hilos, usa llamadas asincrónicas sin bloqueo"?
TheMeaningfulEngineer
1
Si observa los bucles de eventos para una biblioteca como GTK +, verifican si hay nuevos eventos y luego llaman a los controladores de eventos en un bucle, pero si no hay eventos, bloquean un semáforo o un temporizador o algo así. Los desarrolladores individuales crean sus propios bucles de eventos que no se bloquean en una cola de eventos vacía, pero las bibliotecas ampliamente utilizadas se bloquean. Los bucles de eventos son demasiado ineficientes de lo contrario.
Karl Bielefeldt
1
Supongo que podría verlo de esa manera, pero es multihilo en la biblioteca y / o el sistema operativo en lugar del código de la aplicación. Los bucles de eventos abstraen los problemas de sincronización para el desarrollador de la aplicación.
Karl Bielefeldt
13

Pienso en un oyente de eventos no como una función que ejecuta su propio bucle, sino como una carrera de relevos con el primer corredor esperando el arma inicial. Una razón importante para usar eventos en lugar de sondeo es que son más eficientes con los ciclos de la CPU. ¿Por qué? Míralo desde el hardware hacia arriba (en lugar del código fuente hacia abajo).

Considere un servidor web. Cuando su servidor llama listen()y bloquea, su código está tomando su lugar como un corredor de retransmisión. Cuando llega el primer paquete de una nueva conexión, la tarjeta de red comienza la carrera interrumpiendo el sistema operativo. El sistema operativo ejecuta una rutina de servicio de interrupción (ISR) que toma el paquete. El ISR pasa el testigo a una rutina de nivel superior que establece la conexión. Una vez que la conexión está activa, esa rutina pasa el testigo listen()a su código. En ese momento, puede hacer lo que quiera con la conexión. Por lo que sabemos, entre carreras, cada corredor de relevos podría ir al pub. Una de las ventajas de la abstracción de eventos es que su código no tiene que saberlo ni preocuparse.

Algunos sistemas operativos incluyen un código de manejo de eventos que ejecuta su parte de la carrera, se quita el testigo y luego regresa a su punto de partida para esperar a que comience la próxima carrera. En ese sentido, el manejo de eventos es un sondeo optimizado en muchos bucles concurrentes. Sin embargo, siempre hay un disparador externo que inicia el proceso. El detector de eventos no es una función que no regresa, sino una función que está esperando ese disparador externo antes de que se ejecute. Más bien que:

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

Pienso en esto como:

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

y entre el signaly la próxima vez que se ejecuta el controlador, conceptualmente no hay código en ejecución o bucle.

cxw
fuente
Esto tiene sentido, sin embargo, ¿qué está haciendo el bucle de eventos mientras espera la interrupción? Si está bloqueando, ¿no es ese el enfoque 'multihilo' que es el paradigma opuesto al bucle de eventos?
TheMeaningfulEngineer
Si su código tiene el bucle, forever: { select(); do stuff; }entonces volverá a ingresar a la carrera cada vez que pase por el bucle. Ya sea que lo haga repetidamente desde un solo subproceso o en paralelo en subprocesos o procesadores separados, creo que cada evento es su propia raza. Por ejemplo, un navegador web es un programa multiproceso con múltiples bucles de eventos en hilos separados, al menos uno para la interfaz de usuario y uno para cada página que está descargando. La pregunta que hago al codificar es "¿cómo puedo procesar eventos lo suficientemente rápido?" A veces la respuesta es un bucle, a veces hilos, a menudo una combinación.
cxw
He estado haciendo programación basada en eventos durante más de un par de décadas, y encontré esta respuesta muy confusa. Para que un oyente funcione, debe haber un bucle que espere un evento y luego lo enrute a todos los oyentes registrados. (Suponiendo que estamos hablando de software en lugar de interrupciones de hardware)
Bryan Oakley
8

No. No es un "sondeo optimizado". Un bucle de eventos utiliza E / S controladas por interrupción en lugar de sondeo.

Los bucles Mientras, Hasta, Para, etc. son bucles de sondeo.

"Encuesta" es el proceso de verificar repetidamente algo. Dado que el código del bucle se ejecuta continuamente, y debido a que es un bucle pequeño y "ajustado", hay poco tiempo para que el procesador cambie las tareas y haga cualquier otra cosa. Casi todos los "bloqueos", "bloqueos", "bloqueos" o como quiera llamarlo cuando la computadora deja de responder, son la manifestación de que el código está atascado en un bucle de sondeo involuntario. La instrumentación mostrará el uso del 100% de la CPU.

Los bucles de eventos controlados por interrupciones son mucho más eficientes que los bucles de sondeo. El sondeo es un uso extremadamente derrochador de los ciclos de CPU, por lo que se hace todo lo posible para eliminarlo o minimizarlo.

Sin embargo, para optimizar la calidad del código, la mayoría de los idiomas intentan utilizar el paradigma del bucle de sondeo lo más cerca posible para los comandos de entrega de eventos, ya que sirven para propósitos funcionalmente similares dentro de un programa. Por lo tanto, dado que el sondeo es la forma más familiar de esperar una pulsación de tecla o algo así, es fácil para los inexpertos usarlo y terminar con un programa que puede funcionar bien por sí mismo, pero nada más funciona mientras se está ejecutando. Se ha "apoderado" de la máquina.

Como se explicó en otras respuestas, en la entrega de eventos impulsados ​​por interrupciones, esencialmente se establece un "indicador" dentro de la CPU y el proceso se "suspende" (no se permite que se ejecute) hasta que otro indicador modifique ese indicador (como el teclado controlador que lo cambia cuando el usuario ha presionado una tecla). Si el indicador es una condición de hardware real, como una línea que se "tira hacia arriba", se llama "interrupción" o "interrupción de hardware". La mayoría, sin embargo, se implementan solo como una dirección de memoria en la CPU o en la memoria principal (RAM) y se denominan "semáforos".

Los semáforos se pueden cambiar bajo el control del software y, por lo tanto, pueden proporcionar un mecanismo de señalización muy rápido y simple entre los procesos del software.

Sin embargo, las interrupciones solo se pueden cambiar por hardware. El uso más ubicuo de las interrupciones es el que se activa a intervalos regulares por el chip de reloj interno. Uno de los innumerables tipos de acciones de software activadas por interrupciones de reloj es el cambio de semáforos.

He dejado mucho de lado pero tuve que parar en alguna parte. Por favor pregunte si necesita más detalles.

DocSalvager
fuente
7

Por lo general, la respuesta es el hardware, el sistema operativo y los subprocesos en segundo plano que no controlas conspiran para que parezca sin esfuerzo. La tarjeta de red recibe algunos datos, genera una interrupción para informar a la CPU. El controlador de interrupciones del sistema operativo lo maneja. Luego, el sistema operativo despierta un subproceso de fondo que no controla (que se creó al registrarse para el evento y que ha estado inactivo desde que se registró para el evento) como parte del manejo del evento y ejecuta su controlador de eventos.

metal de piedra
fuente
7

Voy a ir en contra de todas las otras respuestas que veo hasta ahora y decir "sí" . Creo que las otras respuestas están complicando demasiado las cosas. Desde un punto de vista conceptual, todos los bucles de eventos son esencialmente:

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

Si está tratando de comprender los bucles de eventos por primera vez, pensar en ellos como un bucle simple no hará daño. Algunos marcos subyacentes están esperando que el SO entregue un evento, luego lo enruta a uno o más controladores, luego espera el próximo evento, y así sucesivamente. Eso es realmente todo lo que hay que hacer desde una perspectiva de software de aplicación.

Bryan Oakley
fuente
1
+1 por ser sencillo. Pero el énfasis está en la "espera"; La ejecución de este código se DETENDRÁ en la primera línea del bucle y no continuará hasta que ocurra un evento. Esto es diferente a un bucle de sondeo ocupado que verifica repetidamente un evento hasta que ocurra uno.
Tom Panning
1

No todos los desencadenantes de eventos se manejan en bucles. La forma en que a menudo escribo mis propios motores de eventos sería así:

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
}

Tenga en cuenta que aunque Memo 1 está en un bucle, el bucle es para notificar a cada oyente. El desencadenador de evento en sí no está necesariamente en un bucle.

En teoría, los eventos clave a nivel del sistema operativo pueden usar la misma técnica (aunque creo que a menudo hacen encuestas, solo estoy especulando aquí), siempre que el sistema operativo exponga algún tipo de registerListenerAPI.

Thomas Eding
fuente