【问题标题】:Does using a context manager in a generator may lead to resources leak?在生成器中使用上下文管理器是否会导致资源泄漏?
【发布时间】:2019-11-21 12:06:31
【问题描述】:

我有一个从上下文管理器产生的函数:

def producer(pathname):
    with open(pathname) as f:
        while True:
            chunk = f.read(4)
            if not chunk:
               break
            yield chunk

当生成器完全被消耗时这不是问题,因为在最后一次迭代期间,生成器在 yield 语句之后恢复执行,循环中断,我们很好地退出了上下文管理器。

但是,如果生成器只是部分消耗,并且没有更多的消费者完全消耗它,那么生成器是否会保持暂停状态永远?在这种情况下,我们永远不会退出上下文管理器。这是否意味着该文件将在程序执行的其余部分保持打开状态?或者至少在生成器被垃圾收集之前?这是我应该自己处理的极端情况,还是我可以依靠 Python 运行时及时关闭悬空上下文管理器?


FWIW,我见过Generator and context manager at the same timeHow to use a python context manager inside a generator,但我认为他们并没有真正回答同样的问题。除非我错过了什么?

【问题讨论】:

  • 这是一个问题吗? - 这是“理所当然的”:在这种情况下,上下文管理器会受到生成器的控制,从而服从生成器的“暂停”性质。
  • 感谢@Roman 的评论。 这是一个问题吗? 我想我并没有我希望的那么清楚。我没有质疑上下文管理器被暂停的事实。但如果没有更多消费者使用它,它可能会永远暂停。

标签: python-3.x generator contextmanager


【解决方案1】:

如果您未能使用整个生成器,上下文管理器将不会被清理,直到生成器被垃圾回收,如果涉及引用循环,或者您在非 CPython 解释器上运行,这可能需要相当长的时间.

您可以通过close-ing 生成器迭代器来解决此问题;所有生成器函数在生成的生成器迭代器上提供a close method,该生成器迭代器在其中引发GeneratorExit;异常从with 语句等中冒出,以确保它们被确定性地正确清理。

要使其在保证的时间点发生,您可以use contextlib.closing 来保证生成器本身的关闭:

 from contextlib import closing

 with closing(producer(mypath)) as produced_items:
     for item in produced_items:
         # Do stuff, maybe break loop early

即使你breakreturn 或引发异常,控制produced_itemswith 也会close 它,这反过来又会为其中的with 语句调用清理。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-17
    • 2022-01-03
    • 1970-01-01
    • 2023-01-14
    • 2010-12-22
    • 1970-01-01
    • 2013-03-27
    • 2011-01-17
    相关资源
    最近更新 更多