我有一个在一段时间后需要取消的asyncio.Task。在取消之前,任务需要进行一些清理。根据文档,我应该可以调用task.cancel或asyncio.wait_for(coroutine, delay)并截获协同过程中的asyncio.TimeoutError,但是下面的示例不起作用。我试过截取其他错误,然后调用task.cancel,但都没有成功。我是不是误解了取消任务的工作原理?

@asyncio.coroutine
def toTimeout():
  try:
    i = 0
    while True:
      print("iteration ", i, "......"); i += 1
      yield from asyncio.sleep(1)
  except asyncio.TimeoutError:
    print("timed out")

def main():
  #... do some stuff
  yield from asyncio.wait_for(toTimeout(), 10)
  #... do some more stuff

asyncio.get_event_loop().run_until_complete(main())
asyncio.get_event_loop().run_forever()

最佳答案

documentation for asyncio.wait_for指定它将取消基础任务,然后从TimeoutError调用本身提升wait_for
返回未来或协同程序的结果。当超时发生时
取消任务并引发asyncio.TimeoutError
任务取消是正确的:
[Task.cancel]安排将CancelledError放入包装的
在事件循环的下一个循环中协同工作。那联程
有机会清理甚至拒绝使用
try/except/finally
注意,文档指定CancelledError被抛出到coroutine中,而不是TimeoutError
如果你做了这样的调整,事情就会按照你的预期进行:

import asyncio

@asyncio.coroutine
def toTimeout():
  try:
    i = 0
    while True:
      print("iteration ", i, "......"); i += 1
      yield from asyncio.sleep(1)
  except asyncio.CancelledError:
    print("timed out")

def main():
  #... do some stuff
  yield from asyncio.wait_for(toTimeout(), 3)
  #... do some more stuff

asyncio.get_event_loop().run_until_complete(main())

输出:
iteration  0 ......
iteration  1 ......
iteration  2 ......
timed out
Traceback (most recent call last):
  File "aio.py", line 18, in <module>
    asyncio.get_event_loop().run_until_complete(main())
  File "/usr/lib/python3.4/asyncio/base_events.py", line 316, in run_until_complete
    return future.result()
  File "/usr/lib/python3.4/asyncio/futures.py", line 275, in result
    raise self._exception
  File "/usr/lib/python3.4/asyncio/tasks.py", line 238, in _step
    result = next(coro)
  File "aio.py", line 15, in main
    yield from asyncio.wait_for(toTimeout(), 3)
  File "/usr/lib/python3.4/asyncio/tasks.py", line 381, in wait_for
    raise futures.TimeoutError()
concurrent.futures._base.TimeoutError

如您所见,现在'timed out'TimeoutError引发wait_for之前被打印出来。

10-06 05:15