【问题标题】:How to gracefully end asyncio program with CTRL-C when using loop run_in_executor使用循环 run_in_executor 时如何使用 CTRL-C 优雅地结束异步程序
【发布时间】:2022-02-16 15:48:50
【问题描述】:

以下代码需要按 3 次 CTRL-C 才能结束,我怎样才能让它只以一次结束? (所以它在 Docker 中运行良好)

import asyncio
import time


def sleep_blocking():
    print("Sleep blocking")
    time.sleep(1000)


async def main():
    loop = asyncio.get_event_loop()
    await loop.run_in_executor(None, sleep_blocking)


try:
    asyncio.run(main())
except KeyboardInterrupt:
    print("Nicely shutting down ...")

我已经阅读了许多与 asyncio 相关的问题和答案,但还没有弄清楚这一点。第一个 CTRL-C 什么都不做,第二个打印“很好地关闭...”然后挂起。第三个 CTRL-C 打印一个丑陋的错误。

我使用的是 Python 3.9.10 和 Linux。

(编辑:每条评论更新代码@mkrieger1)

【问题讨论】:

  • 如果将loop.run_until_complete(loop.run_in_executor(...)) 替换为await loop.run_in_executor(...) 会发生什么?
  • @mkrieger1 那么它需要 3 个 CTRL-C :) 真的。第一个似乎没有做任何事情,第二个打印"Nicely shutting down ...",第三个又是丑陋的错误

标签: python python-asyncio


【解决方案1】:

立即无条件退出 Python 程序的方法是调用 os._exit()。如果您的后台线程正在做一些重要的事情,这可能是不明智的。但是,以下程序会按照您的要求进行操作(python 3.10,Windows10):

import asyncio
import time
import os


def sleep_blocking():
    print("Sleep blocking")
    time.sleep(1000)


async def main():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(loop.run_in_executor(None, sleep_blocking))


try:
    asyncio.run(main())
except KeyboardInterrupt:
    print("Nicely shutting down ...")
    os._exit(42)

【讨论】:

  • 42 是否意味着除“非 0”之外的任何特定内容?
  • 不,函数必须得到一个整数参数。所以我选了一个有名的。
  • 如此简单....太棒了!问题,我在os 文档中看到了这一点:The standard way to exit is sys.exit(n). _exit() should normally only be used in the child process after a fork(). 您推荐 os._exit 的任何理由?
  • 回答我的问题:sys.exit(42) 不会结束程序,它会挂起并且仍然需要多个 CTRL-C
  • 对。 sys.exit() 将阻塞,直到所有辅助的非守护线程都关闭。我看不到任何将辅助线程标记为守护进程的方法,因为它们是由 multiprocessing.Pool 创建的,它不会给你这种可能性。
【解决方案2】:

here 我们知道实际上不可能杀死在线程执行器中运行的任务。如果我用ProcessPoolExecutor 替换默认线程执行程序,我会得到您正在寻找的行为。代码如下:

import concurrent.futures
import asyncio
import time


def sleep_blocking():
    print("Sleep blocking")
    time.sleep(1000)


async def main():
    loop = asyncio.get_event_loop()
    x = concurrent.futures.ProcessPoolExecutor()
    await loop.run_in_executor(x, sleep_blocking)


try:
    asyncio.run(main())
except KeyboardInterrupt:
    print("Nicely shutting down ...")

结果是:

$ python asynctest.py
Sleep blocking
^CNicely shutting down ...

【讨论】:

  • 谢谢,这行得通。但是......我的非人为真实代码实际上也在使用asyncio.run_coroutine_threadsafe,这在另一个进程中不起作用。
  • 您可能需要考虑使用可重现您尝试解决的问题的示例代码来更新您的问题。
猜你喜欢
  • 2018-07-11
  • 2018-01-10
  • 2016-01-18
  • 2014-08-17
  • 1970-01-01
  • 2012-07-01
  • 2018-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多