stringtranslate.com

Bucle de eventos

En informática , el bucle de eventos es una construcción de programación o patrón de diseño que espera y envía eventos o mensajes en un programa . El bucle de eventos funciona realizando una solicitud a algún "proveedor de eventos" interno o externo (que generalmente bloquea la solicitud hasta que llega un evento) y luego llama al controlador de eventos correspondiente ("despacha el evento"). El bucle de eventos también se denomina a veces despachador de mensajes , bucle de mensajes , bomba de mensajes o bucle de ejecución .

También es un método común utilizado para implementar servidores como servidores web .

El bucle de eventos se puede utilizar junto con un reactor , si el proveedor de eventos sigue la interfaz del archivo, que se puede seleccionar o "sondear" (la llamada al sistema Unix, no el sondeo real ). El bucle de eventos casi siempre funciona de forma asincrónica con el originador del mensaje.

Cuando el bucle de eventos forma la construcción de flujo de control central de un programa, como suele ocurrir, se le puede denominar bucle principal o bucle de eventos principal . Este título es apropiado porque dicho bucle de eventos se encuentra en el nivel más alto de control dentro del programa.

Paso de mensajes

Se dice que los bombeos de mensajes "bombean" mensajes desde la cola de mensajes del programa (asignada y generalmente propiedad del sistema operativo subyacente) al programa para su procesamiento. En el sentido más estricto, un bucle de eventos es uno de los métodos para implementar la comunicación entre procesos . De hecho, el procesamiento de mensajes existe en muchos sistemas, incluido un componente a nivel de kernel del sistema operativo Mach . El bucle de eventos es una técnica de implementación específica de sistemas que utilizan el paso de mensajes .

Diseños alternativos

Este enfoque contrasta con otras alternativas:

Uso

Debido al predominio de las interfaces gráficas de usuario , la mayoría de las aplicaciones modernas cuentan con un bucle principal. La get_next_message()rutina normalmente la proporciona el sistema operativo y se bloquea hasta que hay un mensaje disponible. Por lo tanto, sólo se ingresa al bucle cuando hay algo que procesar.

función principal inicializar() mientras mensaje! = salir mensaje := get_next_message() mensaje_proceso(mensaje) finalizar  mientras finaliza  la función

Interfaz de archivos

En Unix , el paradigma " todo es un archivo " conduce naturalmente a un bucle de eventos basado en archivos. La lectura y escritura de archivos, la comunicación entre procesos, la comunicación de red y el control de dispositivos se logran mediante E/S de archivos, con el objetivo identificado mediante un descriptor de archivo . Las llamadas al sistema de selección y sondeo permiten monitorear un conjunto de descriptores de archivos para detectar un cambio de estado, por ejemplo, cuando los datos están disponibles para ser leídos.

Por ejemplo, considere un programa que lee un archivo continuamente actualizado y muestra su contenido en el sistema X Window , que se comunica con los clientes a través de un socket (ya sea dominio Unix o Berkeley ):

def  main ():  file_fd  =  open ( "logfile.log" )  x_fd  =  open_display ()  construct_interface ()  while  True :  rlist ,  _ ,  _  =  select . seleccione ([ file_fd ,  x_fd ],  [],  []):  si  file_fd  en  rlist :  datos  =  file_fd . leer ()  append_to_display ( datos )  send_repaint_message ()  si  x_fd  en  rlist :  proceso_x_messages ()

Manejo de señales

Una de las pocas cosas en Unix que no se ajusta a la interfaz de archivos son los eventos asincrónicos ( señales ). Las señales se reciben en manejadores de señales , pequeños fragmentos de código limitados que se ejecutan mientras el resto de la tarea está suspendida; si se recibe y maneja una señal mientras la tarea está bloqueada select(), select regresará temprano con EINTR ; Si se recibe una señal mientras la tarea está vinculada a la CPU , la tarea se suspenderá entre instrucciones hasta que regrese el controlador de señales.

Por lo tanto, una forma obvia de manejar señales es que los manejadores de señales establezcan una bandera global y hagan que el bucle de eventos verifique la bandera inmediatamente antes y después de la select()llamada; si está configurado, maneje la señal de la misma manera que con los eventos en los descriptores de archivos. Desafortunadamente, esto da lugar a una condición de carrera : si llega una señal inmediatamente entre verificar la bandera y llamar select(), no será manejada hasta que select()regrese por algún otro motivo (por ejemplo, ser interrumpido por un usuario frustrado).

La solución a la que llegó POSIX es la pselect()llamada, que es similar select()pero toma un parámetro adicional sigmask, que describe una máscara de señal . Esto permite que una aplicación enmascare señales en la tarea principal y luego elimine la máscara mientras dure la select()llamada, de modo que los controladores de señales solo se llamen mientras la aplicación está vinculada a E/S . Sin embargo, las implementaciones de pselect()no siempre han sido confiables; Las versiones de Linux anteriores a la 2.6.16 no tienen una pselect()llamada al sistema, [1] se pretende evitar forzar a glibc a emularlo mediante un método propenso a la misma condición de carrera .pselect()

Una solución alternativa, más portátil, es convertir eventos asincrónicos en eventos basados ​​en archivos usando el truco self-pipe , [2] donde "un manejador de señales escribe un byte en una tubería cuyo otro extremo es monitoreado select()en el programa principal". [3] En la versión 2.6.22 del kernel de Linuxsignalfd() , se agregó una nueva llamada al sistema , que permite recibir señales a través de un descriptor de archivo especial.

Implementaciones

HTML/Javascript

Una página web y su JavaScript normalmente se ejecutan en un proceso de navegador web de un solo subproceso . El proceso del navegador maneja los mensajes de una cola de uno en uno. Una función de JavaScript u otro evento del navegador puede estar asociado con un mensaje determinado. Cuando el proceso del navegador ha finalizado con un mensaje, pasa al siguiente mensaje en la cola.

aplicaciones de windows

En el sistema operativo Microsoft Windows , un proceso que interactúa con el usuario debe aceptar y reaccionar a los mensajes entrantes, lo que casi inevitablemente se hace mediante un bucle de mensajes en ese proceso. En Windows, un mensaje equivale a un evento creado e impuesto al sistema operativo. Un evento puede ser interacción del usuario, tráfico de red, procesamiento del sistema, actividad del temporizador, comunicación entre procesos, entre otros. Para eventos no interactivos de solo E/S, Windows tiene puertos de finalización de E/S . Los bucles del puerto de finalización de E/S se ejecutan por separado del bucle de mensajes y no interactúan con el bucle de mensajes de fábrica.

El "corazón" de la mayoría de las aplicaciones Win32 es la función WinMain(), que llama a GetMessage() en un bucle. GetMessage() bloquea hasta que se recibe un mensaje o "evento" (con la función PeekMessage() como alternativa sin bloqueo). Después de algún procesamiento opcional, llamará a DispatchMessage(), que envía el mensaje al controlador correspondiente, también conocido como WindowProc . Normalmente, los mensajes que no tienen un WindowProc() especial se envían a DefWindowProc , el predeterminado. DispatchMessage() llama al WindowProc del identificador HWND del mensaje (registrado con la función RegisterClass()).

Orden de mensajes

Las versiones más recientes de Microsoft Windows garantizan al programador que los mensajes se entregarán al bucle de mensajes de una aplicación en el orden en que fueron percibidos por el sistema y sus periféricos. Esta garantía es esencial al considerar las consecuencias del diseño de aplicaciones multiproceso .

Sin embargo, algunos mensajes tienen reglas diferentes, como mensajes que siempre se reciben al final o mensajes con una prioridad documentada diferente. [4]

Sistema de ventana X

Bucle de eventos Xlib

Las aplicaciones X que utilizan Xlib directamente se basan en la XNextEventfamilia de funciones; XNextEventse bloquea hasta que aparece un evento en la cola de eventos, después de lo cual la aplicación lo procesa adecuadamente. El bucle de eventos Xlib sólo maneja eventos del sistema de ventanas; las aplicaciones que necesitan poder esperar en otros archivos y dispositivos podrían construir su propio bucle de eventos a partir de primitivos como ConnectionNumber, pero en la práctica tienden a utilizar subprocesos múltiples .

Muy pocos programas utilizan Xlib directamente. En el caso más común, los kits de herramientas GUI basados ​​en Xlib generalmente admiten la adición de eventos. Por ejemplo, los kits de herramientas basados ​​en Xt Intrinsics tienen XtAppAddInput()y XtAppAddTimeout().

Tenga en cuenta que no es seguro llamar a funciones Xlib desde un controlador de señales, porque la aplicación X puede haber sido interrumpida en un estado arbitrario, por ejemplo, dentro de XNextEvent. Consulte [1] para obtener una solución para X11R5, X11R6 y Xt.

Bucle de eventos GLib

El bucle de eventos GLib se creó originalmente para su uso en GTK , pero ahora también se usa en aplicaciones que no son GUI, como D-Bus . El recurso encuestado es la colección de descriptores de archivos que le interesan a la aplicación; el bloque de sondeo se interrumpirá si llega una señal o expira un tiempo de espera (por ejemplo, si la aplicación ha especificado un tiempo de espera o una tarea inactiva). Si bien GLib tiene soporte integrado para descriptores de archivos y eventos de terminación secundaria, es posible agregar un origen de evento para cualquier evento que pueda manejarse en un modelo de preparación-verificación-despacho.[2]

Las bibliotecas de aplicaciones que se crean en el bucle de eventos GLib incluyen GStreamer y los métodos de E/S asíncronos de GnomeVFS , pero GTK sigue siendo la biblioteca cliente más visible. Los eventos del sistema de ventanas (en X , leídos en el socket X ) son traducidos por GDK en eventos GTK y emitidos como señales GLib en los objetos widget de la aplicación.

Bucles de ejecución de macOS Core Foundation

Se permite exactamente un CFRunLoop por subproceso y se pueden adjuntar arbitrariamente muchas fuentes y observadores. Luego, las fuentes se comunican con los observadores a través del bucle de ejecución, organizando así la cola y el envío de mensajes.

CFRunLoop se abstrae en Cocoa como NSRunLoop, lo que permite que cualquier mensaje (equivalente a una llamada de función en tiempos de ejecución no reflexivos ) se ponga en cola para enviarse a cualquier objeto.

Ver también

Referencias

  1. ^ "Linux_2_6_16: novatos en el kernel de Linux". kernelnewbies.org . Consultado el 3 de marzo de 2021 .
  2. ^ DJ Bernstein. "El truco de la autopipa".
  3. ^ ERRORES pselect(2): multiplexación de E/S síncrona -  Manual del programador de Linux - Llamadas al sistema
  4. ^ Función GetMessage() con lista de prioridad de mensajes.

enlaces externos