协同程序和资源获取的结合似乎可能会产生一些意想不到的(或不直观的)后果.
基本问题是这样的事情是否有效:
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.. 22
我真的不明白你所询问的是什么冲突,也不知道这个例子的问题:对同一个文件有两个共存的独立句柄是没问题的.
有一件事我不知道我在回答你的问题时学到了它对生成器有一个新的close()方法:
close()
GeneratorExit
在生成器内引发一个新异常以终止迭代.收到此异常后,生成器的代码必须提升GeneratorExit
或StopIteration
.
close()
当生成器被垃圾收集时调用,这意味着生成器的代码在生成器被销毁之前获得最后运行的机会.最后一次机会意味着try...finally
现在可以保证发电机中的报表有效; 该finally
条款现在总是有机会运行.这似乎是语言琐事的一小部分,但使用生成器try...finally
实际上是必要的,以实现with
PEP 343所描述的语句.http://docs.python.org/whatsnew/2.5.html#pep-342-new-generator-features
因此,它处理with
在生成器中使用语句的情况,但它在中间产生但从不返回 - __exit__
当生成器被垃圾收集时,将调用上下文管理器的方法.
编辑:
关于文件句柄问题:我有时会忘记存在不像POSIX的平台.:)
就锁定而言,我认为RafałFowgird在说"你只需要知道发电机就像任何其他拥有资源的物体一样." 我不认为这个with
语句在这里真的那么相关,因为这个函数遇到了同样的死锁问题:
def coroutine(): lock.acquire() yield 'spam' yield 'eggs' lock.release() generator = coroutine() generator.next() lock.acquire() # whoops!
Rafał Dowgir.. 9
我认为不存在真正的冲突.您只需要知道生成器就像保存资源的任何其他对象一样,因此创建者有责任确保它已正确完成(并避免与对象持有的资源发生冲突/死锁).我在这里看到的唯一(次要)问题是生成器没有实现上下文管理协议(至少从Python 2.5开始),所以你不能只:
with coroutine() as cr: doSomething(cr)
但反而必须:
cr = coroutine() try: doSomething(cr) finally: cr.close()
垃圾收集器close()
无论如何都会这样做,但依靠它来释放资源是不好的做法.
我真的不明白你所询问的是什么冲突,也不知道这个例子的问题:对同一个文件有两个共存的独立句柄是没问题的.
有一件事我不知道我在回答你的问题时学到了它对生成器有一个新的close()方法:
close()
GeneratorExit
在生成器内引发一个新异常以终止迭代.收到此异常后,生成器的代码必须提升GeneratorExit
或StopIteration
.
close()
当生成器被垃圾收集时调用,这意味着生成器的代码在生成器被销毁之前获得最后运行的机会.最后一次机会意味着try...finally
现在可以保证发电机中的报表有效; 该finally
条款现在总是有机会运行.这似乎是语言琐事的一小部分,但使用生成器try...finally
实际上是必要的,以实现with
PEP 343所描述的语句.http://docs.python.org/whatsnew/2.5.html#pep-342-new-generator-features
因此,它处理with
在生成器中使用语句的情况,但它在中间产生但从不返回 - __exit__
当生成器被垃圾收集时,将调用上下文管理器的方法.
编辑:
关于文件句柄问题:我有时会忘记存在不像POSIX的平台.:)
就锁定而言,我认为RafałFowgird在说"你只需要知道发电机就像任何其他拥有资源的物体一样." 我不认为这个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()
无论如何都会这样做,但依靠它来释放资源是不好的做法.