python - 联合异步迭代器会发生什么?

标签 python iterator python-asyncio async-iterator

假设我有以下功能

async def f1():
    async for item in asynciterator():
        return

之后异步迭代器会发生什么

await f1()

?我应该担心清理工作,还是发电机在看不见时会以某种方式被垃圾收集?

最佳答案

Should I worry about cleaning up or will the generator be somehow garbage collected when it goes out of sight?

TL;DR Python 的 gc 和 asyncio 将确保最终清理未完全迭代的异步生成器。

这里的“清理”指的是运行yield周围的finally指定的代码,或者上下文管理器的__aexit__部分在 yield 周围的 with 语句中使用。例如,这个简单生成器中的 printaiohttp.ClientSession 用于关闭其资源的相同机制调用:

async def my_gen():
    try:
        yield 1
        yield 2
        yield 3
    finally:
        await asyncio.sleep(0.1)  # make it interesting by awaiting
        print('cleaned up')

如果你运行一个遍历整个生成器的协程,清理将立即执行:

>>> async def test():
...     gen = my_gen()
...     async for _ in gen:
...         pass
...     print('test done')
... 
>>> asyncio.get_event_loop().run_until_complete(test())
cleaned up
test done

请注意清理是如何在循环后立即执行的,即使生成器仍在范围内而没有机会进行垃圾收集。这是因为 async for 循环确保异步生成器在循环耗尽时进行清理。

问题是当循环没有耗尽时会发生什么:

>>> async def test():
...     gen = my_gen()
...     async for _ in gen:
...         break  # exit at once
...     print('test done')
... 
>>> asyncio.get_event_loop().run_until_complete(test())
test done

此处 gen 超出了范围,但根本没有进行清理。如果你用一个普通的生成器尝试这个,清理会立即被引用调用(尽管仍然是 aftertest 退出,因为那时候正在运行的生成器不是不再提及),这是可能的,因为 gen 不参与循环:

>>> def my_gen():
...     try:
...         yield 1
...         yield 2
...         yield 3
...     finally:
...         print('cleaned up')
... 
>>> def test():
...     gen = my_gen()
...     for _ in gen:
...         break
...     print('test done')
... 
>>> test()
test done
cleaned up

my_gen 是一个异步 生成器,它的清理也是异步的。这意味着它不能仅由垃圾收集器执行,它需要由事件循环运行。为了使这成为可能,asyncio registers asyncgen 终结器 Hook ,但它从来没有机会执行,因为我们使用的是 run_until_complete,它会在执行协程后立即停止循环。

如果我们尝试更多地旋转相同的事件循环,我们会看到执行的清理:

>>> asyncio.get_event_loop().run_until_complete(asyncio.sleep(0))
cleaned up

在普通的 asyncio 应用程序中,这不会导致问题,因为事件循环通常与应用程序运行一样长。如果没有事件循环来清理异步生成器,则可能意味着该进程无论如何都会退出。

关于python - 联合异步迭代器会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53210140/

相关文章:

python - 如何支持传入 GAE 电子邮件的不同处理程序?

c++14 - vec.begin() 和 begin(vec) 之间有什么区别

python - Python 异步函数中的多个 Await

python - Jupyter、asyncio 和 Slack

python - 共享 python 生成器

python - 如何执行一个不为序列中的每个项目并行返回任何内容的操作?

python - 运行时错误 : The current Numpy installation fails to pass a sanity check due to a bug in the windows runtime

Python - 使用正则表达式编写模仿 strip() 方法的函数

c++ - 为什么 std::iterator_traits 找不到 value_type?

java - 同一对象的迭代器不独立运行