如何打断龙卷风协程 - python

假设我有两个像这样工作的函数:

@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)。但这不是重点。

它仍然不能解决中断现有协程的问题。即使协程启动的功能已退出,协程仍会继续运行。

编辑:协程取消似乎在Tornado中并没有真正得到支持,这与Python的asyncio不同,在Python中,您可以轻松地在每个屈服点抛出CancelledError

python大神给出的解决方案

如果您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获取Event类。龙卷风4.2将包含事件see the changelog。