asyncio之接收Unix信号

接收Unix信号

Unix系统事件通知通常会中断应用程序,触发其处理程序。当与asyncio一起使用时,信号处理程序回调会与事件循环管理的其他协程和回调交错。这导致中断的功能更少,因此需要提供安全防护来清理不完整的操作。信号处理程序必须是常规可回调函数,而不是协程。

1
2
3
4
5
6
7
8
import asyncio
import functools
import os
import signal
def signal_handler(name):
print('signal_handler({!r})'.format(name))

信号处理程序使用add_signal_handler()注册。第一个参数是信号,第二个是回调函数。回调函数没有参数,因此如果需要参数,函数可以用functools.partial()来包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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'),
)

本示例程序使用协程通过os.kill()向自己发送信号。在发送每个信号之后,协程会让出控制权以允许处理程序运行。在一个正常的应用程序中,应用程序代码将控制权交给事件循环的地方会更多,并且不需要像这样的人工交出控制权。

1
2
3
4
5
6
7
8
9
10
11
12
13
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

主程序运行send_signals(),直到它发送了所有的信号。

1
2
3
4
try:
event_loop.run_until_complete(send_signals())
finally:
event_loop.close()

输出显示send_signals()在发送信号后交出控制权时如何调用处理程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ 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')

本文翻译自《The Python3 Standard Library By Example》asyncio相关章节

文章目录
|