显式关闭asyncio事件循环的必要性

19

故事:

我目前正在查看asyncio的基本示例,特别是这个 - 最简单的HTTP客户端。主函数启动事件循环,在数据获取完成后运行并关闭事件循环:

def main():
    loop = get_event_loop()
    try:
        body = loop.run_until_complete(fetch())
    finally:
        loop.close()
    print(body.decode('latin-1'), end='')

但是,如果我省略 loop.close() 代码,程序也能正常运行:

def main():
    loop = get_event_loop()
    body = loop.run_until_complete(fetch())
    print(body.decode('latin-1'), end='')

问题:

虽然这里有一个例子,但问题是通用的——如果忘记关闭asyncio事件循环,可能会出什么问题?事件循环是否总是会隐式关闭?


2
你会泄露资源。 - Filip Haglund
你认为是什么隐式关闭了它?我认为是垃圾回收,只有在程序结束时才能依赖它,你的程序在完成事件循环后运行多长时间,直到程序结束? - Tadhg McDonald-Jensen
@FilipHaglund 谢谢。你能想到一种测试/演示的方法吗? - alecxe
1
请注意,您提供的链接(https://github.com/python/asyncio/blob/master/examples)是针对Python 3.3的asyncio,并且未更新到Python 3.5 / 3.6。请仅翻译文本内容,不要进行解释。 - Udi
1个回答

13

.close()可以被不同的事件循环实现用来释放事件循环分配的系统资源(或者做其他事情)。如果你查看_UnixSelectorEventLoop的代码,它是Linux中使用的(默认)IOLoop,您将会发现以下代码:

def close(self):
    super().close()
    for sig in list(self._signal_handlers):
        self.remove_signal_handler(sig)
例如,close() 可以移除使用 loop.add_signal_handler() 注册的信号处理器。
由于可以在不同的线程上启动多个IOLoop,或者在关闭旧的IOLoop后创建新的IOLoop(请参阅 asyncio.new_event_loop() ),因此应将其关闭视为一种良好的习惯。 更新 从Python 3.7开始,建议使用asyncio.run代替run_until_complete()
# Python 3.7+
def main():
    body = asyncio.run(fetch())
    print(body.decode('latin-1'), end='')

除其他事项外,asyncio.run 还负责处理 finally 通过调用 close() 关闭循环。


3
自从Python 3.7版本以后,建议使用asyncio.run函数来自动完成这项操作,具体实现可以参考此链接:https://github.com/python/cpython/blob/master/Lib/asyncio/runners.py。 - Udi
1
但是在Windows上,有些情况下(例如使用aiohttp https://github.com/python/cpython/issues/83413),即使在Python 3.9上,asyncio.run也会引发“RuntimeError:Event loop is closed”。 - SerG

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