它是安全的产量从内的一个"与"的方框中Python(和为什么)?
题
该组合的协同程序和资源的获取在看来似乎可能有一些意想不到的(或者不直观)的后果。
基本的问题是,是否或不喜欢的东西这个工作:
def coroutine():
with open(path, 'r') as fh:
for line in fh:
yield line
其它的作用。(你可以测试它!)
深切关注的是, with
应该是什么一种替代 finally
, ,哪里你保证,资源是在该块。协同程序可以暂停和恢复执行从 内 的 with
方框,所以 是如何的冲突解决?
例如,如果你打开文件与读写的内部和外部协程而协程还没有返回:
def coroutine():
with open('test.txt', 'rw+') as fh:
for line in fh:
yield line
a = coroutine()
assert a.next() # Open the filehandle inside the coroutine first.
with open('test.txt', 'rw+') as fh: # Then open it outside.
for line in fh:
print 'Outside coroutine: %r' % repr(line)
assert a.next() # Can we still use it?
更新
我要写信-锁定文件处理竞争的前例,但由于大多数的操作系统分配的文件句柄每过程中将不会有竞争。(荣誉@里指出的例没有太多大意义。) 这是我的修订,例如,其中显示了真正的僵局状况:
import threading
lock = threading.Lock()
def coroutine():
with lock:
yield 'spam'
yield 'eggs'
generator = coroutine()
assert generator.next()
with lock: # Deadlock!
print 'Outside the coroutine got the lock'
assert generator.next()
解决方案
我真的不明白你问什么冲突,也不与示例中的问题:它的罚款,有两种共存,独立手柄相同的文件
有一件事情我不知道,我在回应你的问题是他们得知有发电机上一个新的close()方法:
close()
提高了发生器内部的新GeneratorExit
异常终止迭代。当接收到该异常时,发电机的代码必须要么提高GeneratorExit
或StopIteration
。当发电机是垃圾收集
close()
被调用,所以这意味着发电机的代码获得最后一次机会发电机被破坏之前运行。这最后的机会意味着在发电机try...finally
语句现在可以保证正常工作;在finally
子句现在总是会得到机会运行。这似乎是语言琐事的次要位,但使用发电机和try...finally
实际上是必要的,以便实现通过PEP 343描述的with
语句。http://docs.python.org/ whatsnew / 2.5.html#PEP-342-新 - 发电机 - 功能
这样处理其中with
语句在发电机使用的情况,但在中间产生,但不会返回,在当发电机是垃圾回收情况管理器的__exit__
方法将被调用。
修改强>:
至于文件句柄问题:我有时会忘记,存在不属于POSIX类平台。 :)
至于锁去,我觉得拉法尔Dowgird打在指甲上的头时,他说:“你必须要知道的是,发电机就像是拥有资源的任何其他目的。”我不认为with
语句是真的与此有关,因为该功能从同一僵局的问题而下降:
def coroutine():
lock.acquire()
yield 'spam'
yield 'eggs'
lock.release()
generator = coroutine()
generator.next()
lock.acquire() # whoops!
其他提示
我不认为这是一个真正的冲突。你只需要知道,发电机就像是拥有资源的任何其他对象,所以它是创作者的责任,以确保它是正确完成(并避免与对象持有的资源冲突/死锁)。我在这里看到的唯一的(未成年人)的问题是,发电机不执行上下文管理协议(至少Python 2.5的的),所以你不能只是:
with coroutine() as cr:
doSomething(cr)
,而是必须:
cr = coroutine()
try:
doSomething(cr)
finally:
cr.close()
在垃圾回收器的close()
反正,但它是不好的做法,依靠,对腾出资源。
由于yield
可以执行任意代码,我会非常警惕超过yield语句保持一个锁。你可以在许多其他的方式,不过,包括调用它可能已被覆盖或修改的方法或者功能类似的效果。
发电机,但是,总是(几乎总是)“关闭”,或者与显式close()
呼叫,或者仅仅通过被当作垃圾回收。关闭生成器抛出发电机内GeneratorExit
例外,因此运行最后条款,以声明的清理等,您可以捕获该异常,但你必须抛出或退出功能(即抛出一个异常StopIteration
),而不是产量。它的做法可能是不良,依靠垃圾收集器关闭的情况下,发电机,就像你写的,因为这可以在以后发生的比你可能想,如果有人呼吁sys._exit(),那么你可能会清理不发生在所有
这是我没有料到的事情工作。是的,直到它完成该块不会释放它的资源,所以在这个意义上的资源已经逃脱它的词法嵌套。然而但这是使一个函数调用试图与块中使用相同的资源没有什么不同 - 没有什么可以帮助你的情况下块具有的不的尚未终止,为的任何的原因。这不是真正具体什么发电机。
一两件事,可能是值得担心,虽然是行为若发电机的从不的恢复。我本来期望的with
块像一个finally
块,并呼吁终止__exit__
一部分,但是这似乎并不如此。
一TLDR,看看它是这样的:
with Context():
yield 1
pass # explicitly do nothing *after* yield
# exit context after explicitly doing nothing
的 Context
结束之后 pass
完成(即没什么), pass
执行后 yield
完成(即继续执行).因此, with
结束 后 控制是在继续 yield
.
TLDR:一个 with
上下文中仍然举行了的时候 yield
释放控制。
实际上只有两个规则是相关的:
什么时候
with
释放其资源?它这样做 一旦 并直接 后 其块完成。前者意味着它没有释放 在 一个
yield
, ,因为这可能会发生多次。后来意味着它并释放 后yield
已完成。什么时候
yield
完成了吗?想想
yield
作为反电话:控制传递了一个叫,不下于一个称为一个。同样地,yield
完成当的控制是通过它,就像当一个叫返回的控制。
注意,这两个 with
和 yield
工作为目的在这里!这点 with lock
是为了保护资源和保持受保护期间 yield
.你总是可以明确地释放这种保护:
def safe_generator():
while True:
with lock():
# keep lock for critical operation
result = protected_operation()
# release lock before releasing control
yield result