gpt4 book ai didi

python - 为什么父协程没有取消?

转载 作者:太空宇宙 更新时间:2023-11-04 03:03:02 25 4
gpt4 key购买 nike

我正在玩 Python asyncio。我的程序只有三个协程。其中两个是我直接安排的,而第三个是前者之一安排的。我想在用户按下 Ctrl+C 时正确完成我的程序:

import asyncio

async def coro1():
try:
print('coro1')
await asyncio.sleep(1000)
except Exception as e:
print('coro1 exc %s' % repr(e))
raise

async def coro2():
try:
print('coro2')
await asyncio.ensure_future(coro3())
await asyncio.sleep(1000)
except Exception as e:
print('coro2 exc %s' % repr(e))
raise

async def coro3():
try:
print('coro3')
await asyncio.sleep(1000)
except Exception as e:
print('coro3 exc %s' % repr(e))
raise

loop = asyncio.get_event_loop()
try:
f1 = asyncio.ensure_future(coro1())
f2 = asyncio.ensure_future(coro2())
loop.run_forever()
except KeyboardInterrupt:
print('Exiting... Cancelling all tasks')

f2.cancel()
f1.cancel()

# This code gives the same result:
# for task in asyncio.tasks.Task.all_tasks(loop):
# task.cancel()

print('Cancellation is done!')

loop.stop()
loop.run_forever()
finally:
loop.close()

此代码产生下一个输出:

coro1
coro2
coro3
^CExiting... Cancelling all tasks
Cancellation is done!
coro3 exc CancelledError()
coro1 exc CancelledError()
Task was destroyed but it is pending!
task: <Task pending coro=<coro2() running at test.py:15> wait_for=<Task cancelled coro=<coro3() done, defined at test.py:23>>>

所以我想知道,为什么 coro2 没有被取消而 coro3 实际上 取消了?

最佳答案

明白了!问题出在 except block 中的这两行:

# ...
loop.stop()
loop.run_forever()

预期的取消传播因 loop.stop() 而无效。如果将代码更改为如下内容:

# ...

try:
f1 = asyncio.ensure_future(coro1())
f2 = asyncio.ensure_future(coro2())
loop.run_forever()
except KeyboardInterrupt:
print('Exiting... Cancelling all tasks')
f2.cancel()
f1.cancel()
print('Cancellation is done!')
try:
loop.run_forever()
# Wait a very short time for f2 cancelation and press Ctrl+C again.
except KeyboardInterrupt:
loop.stop()
loop.run_forever()
finally:
loop.close()

消息 Task was destroyed but it is pending! 会消失。

更好的方法是使用 loop.run_until_complete() 方法:

f1 = asyncio.ensure_future(coro1())
f2 = asyncio.ensure_future(coro2())
tasks = asyncio.gather(f1, f2)
try:
loop.run_until_complete(tasks)
except KeyboardInterrupt:
print('Exiting... Cancelling all tasks')
tasks.cancel() # or f1.cancel(); f2.cancel()
print('Cancellation is done!')

loop.run_forever()
tasks.exception() # To skip '_GatheringFuture exception was never retrieved' warning
finally:
loop.close()

run_until_complete 添加将在完成(或取消)所有任务后停止循环的内部回调。

关于python - 为什么父协程没有取消?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40368051/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com