给出以下最小示例:
@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
块运行之前完成。在这个例子中,"finalize gen"
将永远不会被打印出来,因为在此之前程序已经退出了。
请注意,我有意在生成器finally
块中选择了2
的超时,以便上下文管理器finally
有机会先运行。如果我为两个超时选择1
,则将打印两个消息。
这是一种竞争条件吗?我希望所有的finally
块在程序结束之前完成。
如何防止上下文管理器finally
块在生成器finally
块完成之前运行?
背景:
我使用剧作家来控制一个chromium浏览器。外部上下文管理器提供一个在finally
块中关闭的页面。
我使用python3.9.0
.
试试这个例子:https://repl.it/@trixn86/asyncgeneratorraceconcondition
异步上下文管理器不知道异步生成器的任何信息。事实上,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.aclosing
来代替try/finally:
async def main():
async with async_context():
gen = async_gen()
async with contextlib.aclosing(gen):
async for _ in gen:
break