取消 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
我想了解 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