有没有一种类似于使用上下文管理器的 Pythonic 方式在后台运行异步任务?

6

最近我想在运行其他任务时在后台运行一些异步任务,但我认为代码不够“Pythonic”:

task = asyncio.create_task(long_task())
await short_task()
await task

所以,我将其改进得更加符合Python风格:

@asynccontextmanager
async def run_in_background(coro):
    task = asyncio.create_task(coro)
    yield task
    await task


async def main():
    async with run_in_background(long_task()):
        await short_task()

这样的东西已经存在了吗?如果不存在,那么相对于现有的方式,这是否被认为是更Pythonic还是不太Pythonic?


3
将一件短小而有效的东西改造成更大更复杂的东西,这绝对不是Python风格。 - Keith
1
@Keith 它为什么更大更复杂?2行对3行。 - Tom Gringauz
@Keith 这与使用 with open("file")file.open file.close 是一样的。 - Tom Gringauz
好的,由于装饰器的工作方式,您在await之前添加了一个装饰器和一个yield。无论如何,我们中有些人更喜欢Curio的方式。 :-) - Keith
1
@Keith 是的,但那是实现方式,我只需编写一次,然后可以在任何地方使用它。如果已经存在这样的方法,我就不需要编写新函数了。看起来你没有理解我的问题重点。 - Tom Gringauz
3
这只是一般性的观察。这个问题实际上有点含糊不清,因为“Pythonic”也有些主观,并且在异步框架中你并没有真正地在“后台”运行任务。它只是事件循环的一部分,可能会阻塞它。 - Keith
2个回答

5
目前还没有类似的东西,但这是一个非常有用的想法。该概念的更通用版本将在未来 Python 版本中以 TaskGroup 类的形式添加,灵感来自 CurioTrio 的先前作品。
我建议增强实现以使用 finally,提供保证即使出现异常也会等待后台任务的功能;例如:
@asynccontextmanager
async def run_in_background(coro):
    task = asyncio.create_task(coro)
    try:
        yield task
    finally:
        await task

如果不这样做,那么这是否被认为比现有方法更符合Python风格或不符合Python风格?

这个问题显然是基于个人意见的,但我认为上下文管理器更符合Python风格,因为它确保在离开块时后台任务已完成并等待。它还确保后台任务中的异常不会默默地传递,这是asyncio代码中常见的错误来源。


1
谢谢!迫不及待想试用Python 3.8! - Tom Gringauz

1

trio 提供了更好的方法来使用 nursery 对象:

async with trio.open_nursery() as nursery:
    nursery.start_soon(long_task)  # Task will run in background

    await short_task()
    # Wait for background tasks to finish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接