Python asyncio:任务是如何被调度的?

5

我是Python asyncio的新手,正在进行一些实验。我有以下代码:

async def say_after(n, s):
    await asyncio.sleep(n)
    print(s)

async def main():
    task1 = asyncio.create_task(say_after(2, 'a'))
    task2 = asyncio.create_task(say_after(1, 'b'))
    await task1
    print('x', flush=True)
    await task2
    print('y', flush=True)

asyncio.run(main())

输出结果为:

b
a
x
y

我不明白这里的顺序,请有人帮忙解释一下吗?特别是为什么x在b和a之后?


1
如果您尝试运行该代码,实际上只会打印出 xy。如果您等待任务完成,输出结果将是:x y b a - Łukasz Kwieciński
@ŁukaszKwieciński 对不起,我复制了错误版本的代码... 我已经更新了代码,它应该会产生与问题中相同的输出。 - Weiss Zucker
3
在编辑之前我已经使用了我的关闭投票(谢谢,现在更清楚了!),但我认为asyncio.create_task()是干什么用的?才是正确的答案。它会生成新任务;如果你想在一个单独的任务中保持所有操作的顺序,请省略这个调用。 - ggorlen
@ggorlen 谢谢!这个真的帮助我理解了什么是任务。 - Weiss Zucker
3个回答

1
执行say_after(不使用await)会创建一个协程对象,但是尚未启动它
如果您在协程对象上使用await,那么直到Python遇到协程中的awaitreturn或函数结束时,才会执行协程。这里的“执行”指的是将协程转换为Task对象,并将该对象放入异步循环中。
然而,create_task会立即“启动”协程并将其任务放入异步循环中(但由于这是异步而不是并行执行,实际上只有在Python在main()中遇到await时才开始执行)。
在您的情况下,一旦Python看到await task1,它就会“离开”main()并在task1task2之间来回循环(因为两个任务都已经被create_task放入异步循环中),直到task1完成(因为它是被等待的任务)。由于task2已经安排了自己等待的时间更短,所以它首先完成。大约1秒钟后,task1完成,然后才返回到main()(因为请记住,它是在等待task1)。
此时,两个任务都已经完成;await task2行实际上什么都不做;它只是“包装”task2,并(几乎)立即返回。

0
特别是为什么x在b和a之后?“b”在“task2”中打印,大约在“main”开始后1秒钟打印,“a”在“task1”中打印,大约在“main”开始后2秒钟打印。“await task1”等待“task1”,因此等待大约2秒钟。所以到“await task1”完成的时候,“b”和“a”都已经被打印了。(上面的"about"是故意的...虽然会有变化,但在大多数情况下,它们会很小)

0

await task1 基本上是说在 task1 完成之前(即执行 say_after(2, 'a')),不要执行下一行代码。

task1 的执行时间比 task2 更长,因此在你“等待”task1 的时候,task2 已经完成了执行。所以在 await task1 下面放置 await task2 在这里有点无用。如果你交换这两行,输出将会不同。

这就是为什么先打印出 'b' 而不是 'a'。只有在打印出 'a' 之后,才能打印出 'x''y'


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接