为什么我的异步函数没有完成其代码的所有执行?
Why does my asynchronous function not complete all execution of its code?
我刚刚开始学习异步编程,尤其是 async
和 await
语法以及 asyncio
模块的用法。
我的问题是关于以下代码的输出:
import asyncio
async def foo():
print('start fetching')
await asyncio.sleep(2)
print('done fetching')
return {'data':1}
async def boo():
for i in range(10):
print(i)
await asyncio.sleep(0.25)
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
asyncio.run(main())
我得到的输出是:
start fetching
0
虽然我了解代码的每个单独部分的作用,但我不明白为什么只输出了两行代码。这是我的理解:
- 我们从
task1
开始打印 start fetching
.
- 然后我们等待 2 秒。
- 在这两秒内,这不是让
task2
有机会执行 2 秒,所以会打印 0-8 吗?
- 那我也不明白为什么不继续执行函数
foo()
? – 即 print('done fetching')
?
您必须等待任务完成:
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
await task1
await task2
输出:
start fetching
0
1
2
3
4
5
6
7
done fetching
8
9
有关更多示例,请参阅 official document。
现在说说为什么每个函数只打印一行。关键是任何块操作都会导致任务重新调度:
import asyncio
async def foo():
print('start fetching')
# Do not await anything
print('done fetching')
i = 0
# Compute for a long time, but the task scheduler
# will not interrupt it.
while i < 99999999:
i += 1
return {'data':1}
async def boo():
for i in range(10):
print(i)
# Do not await anything
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
asyncio.run(main())
输出:
start fetching
done fetching
0
1
2
3
4
5
6
7
8
9
如您所见,这些任务按顺序完成。
async def foo():
print('start fetching')
# Although it actually not sleep,
# it still force the scheduler to switch tasks.
await asyncio.sleep(0)
print('done fetching')
return {'data':1}
async def boo():
for i in range(10):
print(i)
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
await asyncio.sleep(0)
asyncio.run(main())
输出:
start fetching
0
1
2
3
4
5
6
7
8
9
done fetching
如果任何任务未完成,脚本终止时,那些未完成的任务将被丢弃。
如果您想更深入地了解调度程序,也许您应该阅读 asyncio 的源代码。
asyncio.run()
内部有两个额外的事件循环迭代,它们用于关闭:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.run_until_complete(loop.shutdown_default_executor())
运行 这些调用使之前计划的任务有机会迭代最多 2 次。
注意:这是一个实现细节,随时可能会改变(事件循环startup/shutdown的迭代次数以后可以修改Python 个版本)。
请不要依赖代码中的这种行为,而是明确等待计划任务以正确处理它们。
我刚刚开始学习异步编程,尤其是 async
和 await
语法以及 asyncio
模块的用法。
我的问题是关于以下代码的输出:
import asyncio
async def foo():
print('start fetching')
await asyncio.sleep(2)
print('done fetching')
return {'data':1}
async def boo():
for i in range(10):
print(i)
await asyncio.sleep(0.25)
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
asyncio.run(main())
我得到的输出是:
start fetching
0
虽然我了解代码的每个单独部分的作用,但我不明白为什么只输出了两行代码。这是我的理解:
- 我们从
task1
开始打印start fetching
. - 然后我们等待 2 秒。
- 在这两秒内,这不是让
task2
有机会执行 2 秒,所以会打印 0-8 吗? - 那我也不明白为什么不继续执行函数
foo()
? – 即print('done fetching')
?
您必须等待任务完成:
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
await task1
await task2
输出:
start fetching
0
1
2
3
4
5
6
7
done fetching
8
9
有关更多示例,请参阅 official document。
现在说说为什么每个函数只打印一行。关键是任何块操作都会导致任务重新调度:
import asyncio
async def foo():
print('start fetching')
# Do not await anything
print('done fetching')
i = 0
# Compute for a long time, but the task scheduler
# will not interrupt it.
while i < 99999999:
i += 1
return {'data':1}
async def boo():
for i in range(10):
print(i)
# Do not await anything
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
asyncio.run(main())
输出:
start fetching
done fetching
0
1
2
3
4
5
6
7
8
9
如您所见,这些任务按顺序完成。
async def foo():
print('start fetching')
# Although it actually not sleep,
# it still force the scheduler to switch tasks.
await asyncio.sleep(0)
print('done fetching')
return {'data':1}
async def boo():
for i in range(10):
print(i)
async def main():
task1 = asyncio.create_task(foo())
task2 = asyncio.create_task(boo())
await asyncio.sleep(0)
asyncio.run(main())
输出:
start fetching
0
1
2
3
4
5
6
7
8
9
done fetching
如果任何任务未完成,脚本终止时,那些未完成的任务将被丢弃。
如果您想更深入地了解调度程序,也许您应该阅读 asyncio 的源代码。
asyncio.run()
内部有两个额外的事件循环迭代,它们用于关闭:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.run_until_complete(loop.shutdown_default_executor())
运行 这些调用使之前计划的任务有机会迭代最多 2 次。
注意:这是一个实现细节,随时可能会改变(事件循环startup/shutdown的迭代次数以后可以修改Python 个版本)。
请不要依赖代码中的这种行为,而是明确等待计划任务以正确处理它们。