我正在玩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
实际上被取消了?
答案 0 :(得分:2)
知道了!问题出在except
块中的这两行:
# ...
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
添加内部回调,在完成(或取消)所有任务后停止循环。