取消 asyncio future 有用吗?

Is it ever useful to cancel an asyncio future?

我想了解 Future.cancel() 与 asyncio 的用法。 Python 文档对此非常清楚。我在这里或搜索引擎上的现有问题都没有成功。我只想了解当任务正在等待被取消的未来时会发生什么。

这是我的代码:

import asyncio 

async def foo(future):
    await asyncio.sleep(3)
    future.cancel()

async def bar(future):
    await future
    print("hi")

async def baz(future):
    await bar(future)
    print("ho")

loop = asyncio.get_event_loop()
future = loop.create_future()
loop.create_task(baz(future))
loop.create_task(foo(future))
loop.run_forever()

"hi" 未见打印。所以我最初猜测 bar 在取消的情况下在 await future 行返回。

然而,"ho"也没有被打印出来。因此,取消未来永远不会退回到等待它的任务似乎合乎逻辑?但是这些任务会永远坐在事件循环中吗?这似乎是不可取的,我哪里误解了?

在这种情况下,答案就在文档中,但您必须稍微查找一下。首先,提醒一下等待未来的意义:

# the expression:
x = await future

# is equivalent to:
... magically suspend the coroutine until the future.done() becomes true ...
x = future.result()

换句话说,一旦包含 await 的协程恢复执行,await 语句的值将是等待未来的 result()

问题是:当你取消一个未来时,它的结果是什么? documentation 表示:

If the Future has been cancelled, this method raises a CancelledError exception.

因此,当有人取消您等待的未来时,await future 表达式将引发异常!这巧妙地解释了为什么 bar 不打印 hi(因为 await future 已经加注),以及为什么 baz 不打印 ho(因为 await bar(...) 已提出)。

永远不会打印回溯,因为 loop.create_task 在 "background" (某种)中生成协程 - 如果没有人检查 return 值,异常将丢失。并且由于您丢弃了由 create_task 编辑的 task 对象 return 并使用 run_forever 使循环 运行 永远存在,循环将继续 运行,(永远)等待新任务以某种方式到达。

如果您更改代码以实际收集 bar 的结果,您将很容易观察到 CancelledError:

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    future = loop.create_future()
    loop.create_task(foo(future))
    loop.run_until_complete(baz(future))

输出:

Traceback (most recent call last):
  File "xxx.py", line 19, in <module>
    loop.run_until_complete(baz(future))
  File "/usr/lib/python3.5/asyncio/base_events.py", line 387, in run_until_complete
    return future.result()
  File "/usr/lib/python3.5/asyncio/futures.py", line 266, in result
    raise CancelledError
concurrent.futures._base.CancelledError