Durante una de mis conferencias de hoy sobre Unity, discutimos la actualización de la posición de nuestro jugador verificando cada fotograma si el usuario tiene un botón presionado. Alguien dijo que esto era ineficiente y que deberíamos usar un detector de eventos.
Mi pregunta es, independientemente del lenguaje de programación o la situación en la que se aplica, ¿cómo funciona un oyente de eventos?
Mi intuición supondría que el oyente del evento comprueba constantemente si el evento se ha disparado, lo que significa que, en mi escenario, no sería diferente a verificar cada cuadro si el evento se ha disparado.
Según la discusión en clase, parece que el oyente de eventos funciona de manera diferente.
¿Cómo funciona un oyente de eventos?
event-programming
Gary Holiday
fuente
fuente
Respuestas:
A diferencia del ejemplo de sondeo que proporcionó (donde el botón está marcado en cada cuadro), un detector de eventos no verifica si el botón está presionado. En cambio, se llama cuando se presiona el botón.
Quizás el término "oyente de eventos" te está arrojando. Este término sugiere que el "oyente" está activamente haciendo algo para escuchar, cuando en realidad no está haciendo nada en absoluto. El "oyente" es simplemente una función o método que está suscrito al evento. Cuando se activa el evento, se llama al método de escucha ("controlador de eventos").
El beneficio del patrón de eventos es que no hay costo hasta que se presiona el botón. El evento puede manejarse de esta manera sin ser monitoreado, ya que se origina en lo que llamamos una "interrupción de hardware", que evita brevemente el código en ejecución para activar el evento.
Algunos marcos de UI y juegos usan algo llamado "bucle de mensajes", que pone en cola los eventos para su ejecución en un período de tiempo posterior (generalmente corto), pero aún necesita una interrupción de hardware para llevar ese evento al bucle de mensajes en primer lugar.
fuente
Un oyente de eventos similar a una suscripción a un boletín de correo electrónico (usted se registra para recibir actualizaciones, cuya transmisión es iniciada posteriormente por el remitente), en lugar de actualizar una página web sin cesar (donde usted es quien inicia la transferencia de información).
Se implementa un sistema de eventos utilizando objetos de eventos, que administran una lista de suscriptores. Los objetos interesados (llamados suscriptores , oyentes , delegados , etc.) pueden suscribirse para ser informados de un evento llamando a un método que se suscribe al evento, lo que hace que el evento los agregue a su lista. Cada vez que se dispara el evento (la terminología también puede incluir: llamado , disparado , invocado , ejecutado , etc.), llama al método apropiado en cada uno de los suscriptores, para informarles del evento, transmitiendo cualquier información contextual que necesiten comprender que pasó.
fuente
La respuesta breve e insatisfactoria es que la aplicación recibe una señal (el evento) y que la rutina solo se llama en ese punto.
La explicación más larga es un poco más complicada.
¿De dónde vienen los eventos del cliente?
Cada aplicación moderna † tiene un "bucle de eventos" interno, generalmente semi-oculto, que envía eventos a los componentes correctos que deberían recibirlos. Por ejemplo, se envía un evento de "clic" al botón cuya superficie es visible en las coordenadas actuales del mouse. Esto está en el nivel más simple. En realidad, el sistema operativo realiza gran parte de este envío ya que algunos eventos y algunos componentes recibirán mensajes directamente.
¿De dónde vienen los eventos de aplicación?
Los sistemas operativos envían eventos a medida que ocurren. Lo hacen de manera reactiva al ser notificados por sus propios conductores.
¿Cómo generan eventos los conductores?
No soy un experto, pero seguro que algunos usan interrupciones de la CPU: el hardware que controlan levanta un pin en la CPU cuando hay nuevos datos disponibles; la CPU dispara el controlador que maneja los datos entrantes que eventualmente generan una (cola de) eventos para ser enviados y luego devuelve el control al sistema operativo.
Como puede ver, su aplicación realmente no se está ejecutando todo el tiempo. Es un montón de procedimientos que se activan por el sistema operativo (más o menos) a medida que ocurren los eventos, pero no hace nada el resto del tiempo.
† hay excepciones notables, por ejemplo, juegos por una vez que podrían hacer las cosas de manera diferente
fuente
Terminología
evento : un tipo de cosa que puede suceder.
disparo de eventos : una ocurrencia específica de un evento; Un evento sucediendo.
oyente de eventos : Algo que busca los disparos de eventos.
controlador de eventos : algo que ocurre cuando un escucha de eventos detecta un disparo de evento.
suscriptor de eventos : una respuesta que se supone que debe llamar el controlador de eventos.
Estas definiciones no dependen de la implementación, por lo que se pueden implementar de diferentes maneras.
Algunos de estos términos se confunden comúnmente con sinónimos, ya que a menudo no es necesario que los usuarios distingan entre ellos.
Escenarios comunes
Programación de eventos lógicos.
El evento es cuando se llama a algún método.
Un evento de activación es una llamada particular a ese método.
El detector de eventos es un enlace en el método de evento que se llama en cada disparo de evento que llama al controlador de eventos.
El controlador de eventos llama a una colección de suscriptores de eventos.
El (los) suscriptor (es) del evento realizan cualquier acción (es) que el sistema quiere que ocurra en respuesta a la ocurrencia del evento.
Eventos externos.
El evento es un evento externo que se puede inferir de los observables.
Un evento de activación es cuando ese evento externo puede ser reconocido como ocurrido.
El oyente de eventos detecta de alguna manera los disparos de eventos, a menudo sondeando los observables, luego llama al controlador de eventos al detectar un disparo de eventos.
El controlador de eventos llama a una colección de suscriptores de eventos.
El (los) suscriptor (es) del evento realizan cualquier acción (es) que el sistema quiere que ocurra en respuesta a la ocurrencia del evento.
Sondeo vs. inserción de ganchos en el mecanismo de disparo del evento
El punto hecho por otros es que las encuestas a menudo no son necesarias. Esto se debe a que los oyentes de eventos pueden implementarse haciendo que los disparos de eventos llamen automáticamente al manejador de eventos, que con frecuencia es la forma más eficiente de implementar cosas cuando los eventos son eventos a nivel del sistema.
Por analogía, no es necesario que revise su casilla de correo todos los días si el empleado postal llama a su puerta y le entrega el correo directamente.
Sin embargo, los oyentes de eventos también pueden trabajar sondeando. El sondeo no necesariamente necesita verificar un valor específico u otro observable; Puede ser más complejo. Pero, en general, el objetivo de la encuesta es inferir cuándo se ha producido algún evento para que pueda responderse.
Por analogía, debe revisar su casilla de correo todos los días cuando el empleado postal simplemente tira el correo en ella. No tendría que hacer este trabajo de votación si pudiera indicarle al empleado de correos que llame a su puerta, pero eso a menudo no es una posibilidad.
Encadenamiento de lógica de eventos
En muchos lenguajes de programación, puede escribir un evento que se llama simplemente cuando se presiona una tecla del teclado o en un momento determinado. Aunque se trata de eventos externos, no necesita sondearlos. ¿Por qué?
Es porque el sistema operativo está sondeando por usted. Por ejemplo, Windows busca cosas como cambios de estado del teclado, y si detecta uno, llamará a los suscriptores de eventos. Entonces, cuando se suscribe a un evento de pulsación de teclado, en realidad se está suscribiendo a un evento que es en sí mismo suscriptor de un evento que sondea.
Por analogía, supongamos que vive en un complejo de apartamentos y que un empleado de correos deja el correo en un área de recepción de correo comunitario. Luego, un trabajador similar al sistema operativo puede verificar ese correo para todos, entregando el correo a los apartamentos de aquellos que recibieron algo. Esto ahorra a todos los demás el problema de tener que sondear el área de recepción de correo.
Como sospechaba, un evento puede funcionar mediante encuestas. Y si un evento está relacionado de alguna manera con eventos externos, por ejemplo, presionar una tecla del teclado, entonces el sondeo debe ocurrir en algún momento.
También es cierto que los eventos no necesariamente implican encuestas. Por ejemplo, si el evento es cuando se presiona un botón, entonces el detector de eventos de ese botón es un método que el marco de la GUI podría llamar cuando determina que un clic del mouse golpea el botón. En este caso, el sondeo todavía tenía que suceder para que se detectara el clic del mouse, pero el oyente del mouse es un elemento más pasivo conectado al mecanismo de sondeo primitivo a través del encadenamiento de eventos.
Actualización: en sondeo de hardware de bajo nivel
Resulta que los dispositivos USB y otros protocolos de comunicación modernos tienen un conjunto de protocolos de interacción bastante similar a la red para las interacciones, lo que permite que los dispositivos de E / S, incluidos teclados y ratones, participen en topologías ad hoc .
Curiosamente, las " interrupciones " son cosas sincrónicas bastante imperativas, por lo que no manejan topologías de red ad hoc . Para solucionar esto, las " interrupciones " se han generalizado en paquetes asincrónicos de alta prioridad llamados " transacciones de interrupción " (en el contexto de USB) o " interrupciones señalizadas por mensaje " (en el contexto de PCI). Este protocolo se describe en una especificación USB:
La esencia parece ser que los dispositivos de E / S y los componentes de comunicación (como los concentradores USB) básicamente actúan como dispositivos de red. Entonces, envían mensajes, lo que requiere sondear sus puertos y tal. Esto alivia la necesidad de líneas de hardware dedicadas.
Sistemas operativos como Windows parecen manejar el proceso de votación en sí, por ejemplo, como se describe en la documentación de MSDN para el
USB_ENDPOINT_DESCRIPTOR
's que describe cómo controlar la frecuencia de Windows urnas un controlador de host USB para avisos de alarma / isócronas:Los protocolos de conexión de monitor más nuevos como DisplayPort parecen hacer lo mismo:
Esta abstracción permite algunas características interesantes, como ejecutar 3 monitores desde una conexión:
Conceptualmente, el punto a sacar de esto es que los mecanismos de sondeo permiten comunicaciones en serie más generalizadas, lo cual es increíble cuando se desea una funcionalidad más general. Entonces, el hardware y el sistema operativo realizan muchas encuestas para el sistema lógico. Luego, los consumidores que se suscriben a eventos pueden disfrutar de esos detalles que el sistema de nivel inferior maneja para ellos, sin tener que escribir sus propios protocolos de sondeo / transmisión de mensajes.
En última instancia, los eventos como las pulsaciones de teclas parecen pasar por una serie de eventos bastante interesante antes de llegar al imperativo mecanismo de activación de eventos del nivel de software.
fuente
Pull vs Push
Hay dos estrategias principales para verificar si ocurrió un evento o si se alcanzó un estado específico. Por ejemplo, imagine esperar una entrega importante:
El enfoque de extracción (también llamado sondeo) es más simple: puede implementarlo sin ninguna característica especial. Por otro lado, a menudo es menos eficiente ya que te arriesgas a hacer controles adicionales sin nada que mostrarles.
Por otro lado, el enfoque de inserción es generalmente más eficiente: su código solo se ejecuta cuando tiene algo que hacer. Por otro lado, requiere que exista un mecanismo para que usted registre un oyente / observador / devolución de llamada 1 .
1 Desafortunadamente, mi cartero generalmente carece de dicho mecanismo.
fuente
Sobre la unidad en específico: no hay otra forma de verificar la entrada del jugador que no sea sondearla en cada cuadro. Para crear un detector de eventos, aún necesitaría un objeto como "sistema de eventos" o "administrador de eventos" para realizar el sondeo, por lo que solo llevaría el problema a una clase diferente.
Por supuesto, una vez que tiene un administrador de eventos, solo tiene una clase que sondea la entrada en cada cuadro, pero esto no ofrece ninguna ventaja de rendimiento obvia, ya que ahora esta clase tiene que iterar sobre los oyentes y llamarlos, lo que, dependiendo de su juego El diseño (como en cuántos oyentes hay y con qué frecuencia el jugador usa la entrada), en realidad podría ser más costoso.
Además de todo esto, recuerde la regla de oro: la optimización prematura es la raíz de todo mal , lo cual es especialmente cierto en los videojuegos, donde a menudo el proceso de renderizar cada cuadro cuesta tanto, que las pequeñas optimizaciones de guiones como esta son completamente insignificantes
fuente
A menos que tenga algún soporte en su OS / Framework que maneje eventos como presionar botones o desbordamiento de temporizador o llegada de mensajes, deberá implementar este patrón de escucha de eventos utilizando el sondeo de todos modos (en algún lugar debajo).
Pero no se aleje de este patrón de diseño solo porque no tiene un beneficio de rendimiento allí de inmediato. Estas son las razones por las que debe usarlo independientemente de si tiene soporte subyacente para el manejo de eventos o no.
Conclusión: tuvo la suerte de participar en la discusión y aprendió una alternativa a las encuestas. Busque una oportunidad para aplicar este concepto en la práctica y apreciará lo elegante que puede ser el código.
fuente
La mayoría de los bucles de eventos se construyen por encima de alguna primitiva de multiplexación de sondeo proporcionada por el sistema operativo. En Linux, esa primitiva es a menudo la
poll
(2) llamada al sistema (pero podría ser la anteriorselect
). En las aplicaciones GUI, el servidor de visualización (por ejemplo , Xorg o Wayland ) se está comunicando (a través de un socket (7) o tubería (7) ) con su aplicación. Lea también sobre Arquitectura y protocolos del sistema X Window .Tales primitivas de sondeo son eficientes; el núcleo en la práctica despertaría su proceso cuando se realiza alguna entrada (y se maneja alguna interrupción).
Concretamente, su biblioteca de kit de herramientas de widgets se comunica con su servidor de visualización, espera mensajes y envía estos mensajes a sus widgets. Las bibliotecas de herramientas como Qt o GTK son bastante complejas (millones de líneas de código fuente). Su teclado y mouse solo son manejados por el proceso del servidor de visualización (que traduce tales entradas a mensajes de eventos enviados a las aplicaciones del cliente).
(Estoy simplificando; de hecho, las cosas son mucho más complejas)
fuente
En un sistema puramente basado en encuestas, el subsistema que quiera saber cuándo ocurre una acción en particular necesitará ejecutar algún código cada vez que esa acción pueda ocurrir. Si hay muchos subsistemas que necesitarían reaccionar dentro de los 10 ms de un evento no necesariamente único, todos tendrían que verificar al menos 100 veces / segundo si su evento había ocurrido. Si esos subsistemas se encuentran en procesos de subprocesos diferentes (o peor, procesos), eso requeriría cambiar en cada subproceso o proceso 100x / segundo.
Si muchas de las cosas que verán las aplicaciones son bastante similares, puede ser más eficiente tener un subsistema de monitoreo centralizado, tal vez controlado por tablas, que pueda observar muchas cosas y observar si alguna de ellas ha cambiado. Si hay 32 conmutadores, por ejemplo, una plataforma podría tener una función para leer los 32 conmutadores a la vez en una palabra, haciendo posible que el código del monitor verifique si los conmutadores han cambiado entre encuestas y, si no, no Preocúpese sobre qué código podría estar interesado en ellos.
Si hay muchos subsistemas que desean recibir una notificación cuando algo cambia, hacer que un subsistema de monitoreo dedicado notifique a otros subsistemas cuando ocurren eventos que les interesan puede ser más eficiente que hacer que cada subsistema sondee sus propios eventos. Sin embargo, la creación de un subsistema de monitoreo dedicado en los casos en que nadie esté interesado en ningún evento representaría un puro desperdicio de recursos. Si solo unos pocos subsistemas están interesados en eventos, el costo de hacer que vigilen los eventos que les interesan puede ser menor que el costo de establecer un subsistema de monitoreo dedicado de propósito general, pero el punto de equilibrio El punto variará significativamente entre las diferentes plataformas.
fuente
Un oyente de eventos es como un oído que espera un mensaje. Cuando se produce el evento, la subrutina elegida como detector de eventos funciona utilizando los argumentos del evento.
Siempre hay dos datos importantes: el momento en que ocurre el evento y el objeto donde ocurre este evento. Otro argumento son más datos sobre lo que sucedió.
El detector de eventos especifica la reacción a lo que ocurre.
fuente
Un escucha de eventos sigue el patrón de publicación / suscripción (como suscriptor)
En su forma más simple, un objeto de publicación mantiene una lista de instrucciones de los suscriptores que deben llevarse a cabo cuando algo necesita ser publicado.
Tendrá algún tipo de
subscribe(x)
método, donde x depende de cómo esté diseñado el controlador de eventos para manejar el evento. Cuando se llama a subscribe (x), x se agrega a la lista de editores de las instrucciones / referencias de los suscriptores.El editor puede contener toda, parte o nada de la lógica para manejar el evento. Simplemente puede requerir referencias a los suscriptores para notificarlos / transformarlos con su lógica especificada cuando ocurra el evento. Puede no contener lógica y requerir objetos de suscriptor (métodos / escuchas de eventos) que puedan manejar el evento. Es más probable que contenga una mezcla de ambos.
Cuando ocurre un evento, el editor repetirá y ejecutará su lógica para cada elemento en su lista de instrucciones / referencias de suscriptores.
No importa cuán complejo se vea un controlador de eventos, en esencia sigue este patrón simple.
Ejemplos
Para un ejemplo de escucha de eventos, usted proporciona un método / función / instrucción / escucha de eventos al método subscribe () del controlador de eventos. El controlador de eventos agrega el método a su lista de devoluciones de llamadas de suscriptores. Cuando ocurre un evento, el controlador de eventos itera sobre su lista y ejecuta cada devolución de llamada.
Para un ejemplo del mundo real, cuando se suscribe al boletín en Stack Exchange, se agregará una referencia a su perfil a una tabla de suscriptores de la base de datos. Cuando llegue el momento de publicar el boletín, la referencia se utilizará para completar una plantilla del boletín y se enviará a su correo electrónico. En este caso, x es simplemente una referencia para usted, y el editor tiene un conjunto de instrucciones internas utilizadas para todos los suscriptores.
fuente