WGH*_*WGH 2 python yield exception tornado coroutine
假设我有两个像这样工作的函数:
@tornado.gen.coroutine
def f():
for i in range(4):
print("f", i)
yield tornado.gen.sleep(0.5)
@tornado.gen.coroutine
def g():
yield tornado.gen.sleep(1)
print("Let's raise RuntimeError")
raise RuntimeError
Run Code Online (Sandbox Code Playgroud)
通常,函数f可能包含无限循环并且永不返回(例如,它可以处理某个队列)。
我想要做的是能够在它产生的任何时候中断它。
最明显的方法不起作用。异常仅在函数f退出后引发(如果它是无穷无尽的,它显然永远不会发生)。
@tornado.gen.coroutine
def main():
try:
yield [f(), g()]
except Exception as e:
print("Caught", repr(e))
while True:
yield tornado.gen.sleep(10)
if __name__ == "__main__":
tornado.ioloop.IOLoop.instance().run_sync(main)
Run Code Online (Sandbox Code Playgroud)
输出:
f 0
f 1
Let's raise RuntimeError
f 2
f 3
Traceback (most recent call last):
File "/tmp/test/lib/python3.4/site-packages/tornado/gen.py", line 812, in run
yielded = self.gen.send(value)
StopIteration
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
<...>
File "test.py", line 16, in g
raise RuntimeError
RuntimeError
Run Code Online (Sandbox Code Playgroud)
也就是说,只有当两个协程都返回(两个期货都解决)时才会引发异常。
这部分由 解决tornado.gen.WaitIterator,但它有问题(除非我弄错了)。但这不是重点。
它仍然没有解决中断现有协程的问题。即使启动它的函数退出,协程也会继续运行。
编辑:Tornado 似乎并不真正支持协程取消,这与 Python 的 asyncio 不同,在 Python 的 asyncio 中,您可以轻松地CancelledError在每个屈服点抛出。
如果您根据说明使用 WaitIterator,并使用toro.Event在协程之间发出信号,它会按预期工作:
from datetime import timedelta
import tornado.gen
import tornado.ioloop
import toro
stop = toro.Event()
@tornado.gen.coroutine
def f():
for i in range(4):
print("f", i)
# wait raises Timeout if not set before the deadline.
try:
yield stop.wait(timedelta(seconds=0.5))
print("f done")
return
except toro.Timeout:
print("f continuing")
@tornado.gen.coroutine
def g():
yield tornado.gen.sleep(1)
print("Let's raise RuntimeError")
raise RuntimeError
@tornado.gen.coroutine
def main():
wait_iterator = tornado.gen.WaitIterator(f(), g())
while not wait_iterator.done():
try:
result = yield wait_iterator.next()
except Exception as e:
print("Error {} from {}".format(e, wait_iterator.current_future))
stop.set()
else:
print("Result {} received from {} at {}".format(
result, wait_iterator.current_future,
wait_iterator.current_index))
if __name__ == "__main__":
tornado.ioloop.IOLoop.instance().run_sync(main)
Run Code Online (Sandbox Code Playgroud)
现在,pip install toro获取 Event 类。Tornado 4.2 将包含事件,请参阅更改日志。