Recibir Señales Unix

Las notificaciones de eventos del sistema Unix normalmente interrumpen una aplicación, desencadenando su manejador. Cuando se usa con asyncio, devoluciones de llamada del manejador de señales están intercaladas con las otras co-rutinas y devoluciones de llamada gestionadas por el bucle de eventos. Esto resulta en menos funciones interrumpidas, y la necesidad resultante de proporcionar guardas de seguridad para la limpieza de operaciones incompletas.

asyncio_signal.py
import asyncio
import functools
import os
import signal


def signal_handler(name):
    print('signal_handler({!r})'.format(name))

Los manejadores de señal se registran utilizando add_signal_handler(). El primer argumento es la señal y el segundo es la devolución de llamada. Las devoluciones de llamada se pasan sin argumentos, por lo que si se necesitan argumentos una función puede ser envuelta con functools.partial().

event_loop = asyncio.get_event_loop()

event_loop.add_signal_handler(
    signal.SIGHUP,
    functools.partial(signal_handler, name='SIGHUP'),
)
event_loop.add_signal_handler(
    signal.SIGUSR1,
    functools.partial(signal_handler, name='SIGUSR1'),
)
event_loop.add_signal_handler(
    signal.SIGINT,
    functools.partial(signal_handler, name='SIGINT'),
)

Este programa de ejemplo utiliza una co-rutina para enviarse señales a través de os.kill(). Después de que se envía cada señal, la co-rutina cede el control para permitir que el manejador se ejecute. En una aplicación normal, habría más lugares donde el código de la aplicación cede al bucle de eventos y no se necesitaría un rendimiento artificial como este.

async def send_signals():
    pid = os.getpid()
    print('starting send_signals for {}'.format(pid))

    for name in ['SIGHUP', 'SIGHUP', 'SIGUSR1', 'SIGINT']:
        print('sending {}'.format(name))
        os.kill(pid, getattr(signal, name))
        # Yield control to allow the signal handler to run,
        # since the signal does not interrupt the program
        # flow otherwise.
        print('yielding control')
        await asyncio.sleep(0.01)
    return

El programa principal ejecuta send_signals() hasta que haya enviado todas las señales

try:
    event_loop.run_until_complete(send_signals())
finally:
    event_loop.close()

La salida muestra cómo se llaman los manejadors cuando send_signals() cede el control después de enviar una señal.

$ python3 asyncio_signal.py

starting send_signals for 21772
sending SIGHUP
yielding control
signal_handler('SIGHUP')
sending SIGHUP
yielding control
signal_handler('SIGHUP')
sending SIGUSR1
yielding control
signal_handler('SIGUSR1')
sending SIGINT
yielding control
signal_handler('SIGINT')

Ver también

  • signal – Recibir notificación de eventos asíncronos del sistema.