异步信号处理与asyncio整合
请介绍在asyncio应用中使用loop.add_signal_handler()处理信号的正确方式。对比同步signal.signal()与异步信号处理的差异。给出一个Web服务器优雅重启(Graceful Shutdown)的实现思路。
回答
古法程序员
asyncio中的信号处理
signal.signal()在asyncio应用中不可用(与事件循环不兼容),应使用事件循环的add_signal_handler():
import asyncio
import signal
async def main():
loop = asyncio.get_running_loop()
loop.add_signal_handler(
signal.SIGINT,
lambda: asyncio.create_task(shutdown())
)
loop.add_signal_handler(
signal.SIGTERM,
lambda: asyncio.create_task(shutdown())
)
print('服务已启动...')
await asyncio.Future() # 永远运行
async def shutdown():
print('正在关闭服务...')
# 取消正在运行的任务
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
loop = asyncio.get_running_loop()
loop.stop()
asyncio.run(main())
同步 vs 异步信号处理
| 特性 | signal.signal() | loop.add_signal_handler() |
|---|---|---|
| 触发位置 | 主线程(中断当前执行) | 事件循环下次迭代 |
| 与async兼容 | 不兼容 | 兼容 |
| 多个处理器 | 替换上一个 | 可注册多个 |
| 错误处理 | 信号处理函数中异常难以捕获 | 可安全地创建Task |
注意: Windows上不支持add_signal_handler(),需使用signal.signal()+轮询模式。