【问题标题】:'close' event listener for python subprocess using asyncio使用 asyncio 的 python 子进程的“关闭”事件侦听器
【发布时间】:2020-11-26 11:43:30
【问题描述】:

我正在尝试将事件侦听器附加到 python 子进程,每当进程关闭/退出时都会调用它。回调将重新生成子进程并将相同的事件侦听器附加到新的。

类似于在 node.js 中从 child_process 调用 .on

我已经知道this thread。但理想情况下,我希望使用 asyncio.subprocess 来执行此操作,而不是在单独的线程中运行每个事件侦听器。

我在想这样的事情-

from asyncio import subprocess as asubprocess
from typing import Callable, Awaitable

async def handle_close(proc: asubprocess.Process, spawner: Callable[[], Awaitable[asubprocess.Process]]):
    while True:
        await proc.wait()
        proc = await spawner()

(此函数还可以扩展为获取进程列表及其各自的生成器和所有进程的asyncio.wait,一旦它们中的任何一个停止 - 它们将重新生成并重复该过程)

proc 参数将是由 asubprocess.create_subprocess_exec 返回的进程,spawner 将是重新生成该子进程的异步函数。 (或任何其他回调)

唯一的问题是,我不知道如何在后台运行它而不用async 污染我的整个代码库。理想情况下,会有多个子进程在后台需要它们自己的handle_close。当所有这些处理程序都在运行时,不应阻塞调用者代码。

在我自己的用例中,我不需要进程句柄,因此可以丢弃这些句柄,只要进程继续运行并在它们停止的情况下重新生成并且生成所有进程的调用者代码可以控制执行其他操作,没关系。

【问题讨论】:

    标签: python event-handling subprocess python-asyncio


    【解决方案1】:

    唯一的问题是,我不知道如何在后台运行它而不用async 污染我的整个代码库。

    这句话并不完全清楚实际的约束是什么,即你愿意用异步代码走多远。通常假设使用 asyncio 的程序在 asyncio 事件循环中运行,然后整个程序是异步的,即使用回调和/或协程。但是,如果您有一个使用阻塞代码和线程的大型代码库,您也可以在单独的线程中引入 asyncio。例如:

    _loop = asyncio.new_event_loop()
    def _run():
        asyncio.set_event_loop(_loop)
        _loop.run_forever()
    threading.Thread(target=_run, daemon=True).start()
    
    def async_submit(coro):
        return asyncio.run_coroutine_threadsafe(coro, _loop)
    

    事件循环在后台运行,您可以从阻塞代码向它提交任务。例如:

    from typing import List
    
    async def handle_process(cmd: List[str]):
        while True:
            p = await asubprocess.create_subprocess_exec(*cmd)
            await p.wait()
            print('restarting', cmd)
    
    # tell the event loop running in the background to respawn "sleep 1":
    async_submit(handle_process(["sleep", "1"]))
    

    请注意,与事件循环的所有交互都必须通过run_coroutine_threadsafe 或其表亲call_soon_threadsafe 执行。

    【讨论】:

    • 这似乎很好用!当主线程最终决定关闭运行循环的线程并因此关闭所有进程处理程序时,我应该注意什么?是否应该遍历asyncio.all_tasks(_loop) 来取消它们,然后是_loop.stop()thrd.join() 就足够了吗?假设thrd 是线程句柄
    • @Chase 感谢您修复错字。你可以在关机时调用_loop.call_soon_threadsafe(_loop.stop) 来停止事件循环,然后加入线程,但老实说,我什至不会打扰。虽然取消您产生的任务是 asyncio 中定义明确的操作,但取消 所有 任务,包括那些在内部产生的任务,并不是系统准备好的,可能会导致额外的异常。最重要的是,它不会给您带来任何好处,因为无论如何流程都将退出。
    猜你喜欢
    • 2012-02-01
    • 2011-10-27
    • 2019-12-07
    • 1970-01-01
    • 2013-11-29
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多