CodeWalk

异步信号处理与asyncio整合

作者:古法程序员 · 2026-05-30 12:55

请介绍在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()+轮询模式。