You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Ctrl-C优雅终止asyncio脚本且避免打印回溯信息?

解决asyncio脚本Ctrl-C终止时的回溯问题

我太懂这种挫败感了——折腾了一堆方法还是甩不掉那些烦人的回溯信息,尤其是在想让脚本优雅退出的时候。咱们来一步步把你的脚本改成干净退出的版本,顺便说清楚问题出在哪。

问题根源

你原来的代码用了loop.stop(),但这只是让事件循环停止运行,并没有处理以下关键环节:

  • 正在运行的异步任务没有被取消,可能会残留
  • 事件循环没有被正确关闭和清理资源
  • Python默认的KeyboardInterrupt异常还是会被抛出,导致回溯打印

修改后的完整代码

import asyncio
import datetime
import functools
import signal
import sys

async def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)

def stopper(signame, loop):
    print(f"Got {signame}, stopping...")
    # 取消所有当前运行的任务,让它们能正常终止
    for task in asyncio.all_tasks(loop):
        task.cancel()
    loop.stop()

loop = asyncio.get_event_loop()

# 为SIGINT(Ctrl-C)和SIGTERM注册信号处理函数
for signame in ('SIGINT', 'SIGTERM'):
    loop.add_signal_handler(getattr(signal, signame), functools.partial(stopper, signame, loop))

try:
    # 将display_date任务加入事件循环
    loop.create_task(display_date(loop))
    # 启动事件循环
    loop.run_forever()
except KeyboardInterrupt:
    # 捕获Ctrl-C异常,避免打印回溯信息
    print("KeyboardInterrupt caught, cleaning up...")
finally:
    # 清理异步生成器(如果有的话)
    loop.shutdown_asyncgens()
    # 彻底关闭事件循环
    loop.close()
    print("Loop closed successfully.")

关键改动说明

  1. 任务取消:在信号处理函数里添加了task.cancel(),这样所有正在运行的异步任务会收到CancelledError,像await asyncio.sleep()这类异步操作会自动响应取消,让任务能平稳结束。
  2. 异常捕获:用try-except包裹loop.run_forever(),捕获KeyboardInterrupt异常,直接跳过默认的回溯打印。
  3. 循环清理:finally块里的shutdown_asyncgens()和loop.close()确保所有异步资源都被正确释放,避免内存泄漏或残留进程。

现在你再按Ctrl-C试试,只会看到你自定义的停止提示,再也不会有那些乱糟糟的回溯信息了。

内容的提问来源于stack exchange,提问作者JK Laiho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:10:42