假设我有两个像这样工作的功能:
@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
[通常,函数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)
输出:
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
即,仅当两个协程都返回(两个期货均结算)时才会引发异常。
[这已由tornado.gen.WaitIterator
部分解决,但是它有问题(unless I'm mistaken)。但这不是重点。
它仍然不能解决中断现有协程的问题。即使启动了协程,它也会继续运行。
EDIT:在Tornado中似乎并不真正支持协程取消,这与Python的asyncio不同,在Python中,您可以轻松地在每个屈服点抛出CancelledError
。
如果您use WaitIterator according to the instructions,并在协程之间使用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)
现在,单击pip install toro
以获取事件类。龙卷风4.2将包含事件see the changelog。
使用更原始的东西(例如全局变量)有什么区别?
import asyncio
event = asyncio.Event()
aflag = False
async def short():
while not aflag:
print('short repeat')
await asyncio.sleep(1)
print('short end')
async def long():
global aflag
print('LONG START')
await asyncio.sleep(3)
aflag = True
print('LONG END')
async def main():
await asyncio.gather(long(), short())
if __name__ == '__main__':
asyncio.run(main())
是用于asyncio,但我想这个想法保持不变。这是一个半问题(为什么Event会更好?)。然而,解决方案可以产生作者需要的确切结果:
LONG START
short repeat
short repeat
short repeat
LONG END
short end