【发布时间】:2010-10-15 15:55:42
【问题描述】:
协同程序和资源获取的结合似乎会产生一些意想不到的(或不直观的)后果。
基本问题是这样的事情是否有效:
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?
更新
在前面的示例中,我打算进行写锁定文件句柄争用,但由于大多数操作系统为每个进程分配文件句柄,因此不会出现争用。 (感谢@Miles 指出该示例并没有多大意义。)这是我修改后的示例,它显示了一个真正的死锁情况:
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()
【问题讨论】:
-
@Miles 指出该示例格式有些错误。我想要一个写锁定的文件句柄,但由于操作系统可能会为每个进程分配文件句柄,这会很好。
-
TL;DR
yield和return是安全的(因为它们最终会释放资源)。但是return可能表现不佳。考虑with os.scandir() as entries: return entries。它根本行不通!请改用with os.scandir() as entries: yield from entries或简单的return os.scandir()。如果没有用尽,第二种解决方案将要求您在ScandirIterator实例上调用.close()。这只是一个示例,但它说明了从with语句返回临时资源时会发生什么。
标签: python resources coroutine