1

给定以下最小示例:

@asynccontextmanager
async def async_context():
    try:
        yield
    finally:
        await asyncio.sleep(1)
        print('finalize context')


async def async_gen():
    try:
        yield
    finally:
        await asyncio.sleep(2)
        # will never be called if timeout is larger than in async_context
        print('finalize gen')


async def main():
    async with async_context():
        async for _ in async_gen():
            break


if __name__ == "__main__":
    asyncio.run(main())

我正在break迭代异步生成器,我希望 finally 块在我的异步上下文管理器finally块运行之前完成。在此示例"finalize gen"中,将永远不会打印,因为程序在此之前退出。

请注意,我故意2在生成器finally块中选择了超时,以便上下文管理器finally有机会在之前运行。如果我选择1了两个超时,则将打印两条消息。

这是一种竞赛条件吗?我希望finally在程序完成之前完成所有块。

如何防止上下文管理器块finally在生成器块完成之前运行finally

对于上下文:

我使用剧作家来控制铬浏览器。外部上下文管理器提供它在finally块中关闭的页面。

我正在使用 python 3.9.0

试试这个例子:https ://repl.it/@trixn86/AsyncGeneratorRaceCondition

4

1 回答 1

3

异步上下文管理器对异步生成器一无所知。事实上,在main你之后的异步生成器一无所知。break你已经让自己没有办法等待生成器的完成。

如果要等待生成器关闭,则需要显式处理关闭:

async def main():
    async with async_context():
        gen = async_gen()
        try:
            async for _ in gen:
                break
        finally:
            await gen.aclose()

在 Python 3.10 中,您将能够使用contextlib.aclosingtry/finally 来代替:

async def main():
    async with async_context():
        gen = async_gen()
        async with contextlib.aclosing(gen):
            async for _ in gen:
                break
于 2021-02-24T11:13:45.783 回答