为什么这个asyncio.Task永远不会完成取消?

时间:2015-08-08 02:00:51

标签: python python-3.x concurrency python-asyncio

如果我在 python3 解释器上运行它:

import asyncio

@asyncio.coroutine
def wait(n):
    asyncio.sleep(n)

loop = asyncio.get_event_loop()
fut = asyncio.async(wait(10))
fut.add_done_callback(lambda x: print('Done'))

asyncio.Task.all_tasks()

我得到以下结果:

{<Task pending coro=<coro() running at /usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/coroutines.py:139> cb=[<lambda>() at <ipython-input-5-c72c2da2ffa4>:1]>}

现在,如果我运行fut.cancel(),我会返回True。但是输入fut会返回任务的表示,表明它正在取消

<Task cancelling coro=<coro() running at /usr/local/Cellar/python3/3.4.3/Frameworks/Python.framework/Versions/3.4/lib/python3.4/asyncio/coroutines.py:139> cb=[<lambda>() at <ipython-input-5-c72c2da2ffa4>:1]>

任务从未实际取消(fut.cancelled()永不返回True

为什么不取消?

2 个答案:

答案 0 :(得分:4)

调用task.cancel()仅调度在下次运行事件循环时要取消的任务;它不会立即取消任务,甚至可以保证在事件循环运行下一次迭代时实际取消该任务。这一切都描述为in the documentation

  

取消()

     

请求此任务取消。

     

这会安排将CancelledError扔进被包裹的地方   通过事件循环在下一个循环中的协程。那个协程   有机会清理甚至拒绝使用请求   尝试/除外/最后。

     

Future.cancel()不同,这并不能保证任务的完成   取消:异常可能被抓住并采取行动,推迟   取消任务或完全阻止取消。该   任务也可以返回值或引发不同的异常。

     

调用此方法后,cancelled()将不会立即返回   True (除非该任务已被取消)。任务将标记为   当包装的协程以CancelledError终止时取消   异常(即使未调用cancel())。

在您的情况下,您实际上从未启动过事件循环,因此任务永远不会被取消。您需要致电loop.run_until_complete(fut)(或loop.run_forever(),虽然这不是这个特定情况的最佳选择),但任务实际上最终会被取消。

此外,对于它的价值,使用实际脚本而不是解释器来测试asyncio代码通常更容易,因为必须不断重写协同程序并启动/停止事件循环会变得乏味。

答案 1 :(得分:0)

在解释器中使用 asyncio 测试很棘手,因为python需要保持事件循环不断轮询其任务。

所以测试 asyncio 的一些建议是:

  1. 编写并运行脚本,而不是使用交互式解释器
  2. 在脚本末尾添加loop.run_forever(),以便执行所有任务。
  3. 另一种方法是为您要运行的每个任务运行loop.run_until_complete(coro())
  4. yield from前面有asyncio.sleep(n)所以它实际上可以运行。当前代码返回一个生成器并且什么都不做。