如何中断龙卷风协程

问题描述 投票:2回答:2

假设我有两个像这样工作的功能:

@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

python exception tornado yield coroutine
2个回答
5
投票

如果您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


0
投票

使用更原始的东西(例如全局变量)有什么区别?

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
© www.soinside.com 2019 - 2024. All rights reserved.