【发布时间】:2020-12-06 20:59:51
【问题描述】:
据我了解,asyncio.gather 旨在并发运行其参数,并且当协程执行等待表达式时,它为事件循环提供了调度其他任务的机会。考虑到这一点,我惊讶地发现以下 sn-p 忽略了 asyncio.gather 的输入之一。
import asyncio
async def aprint(s):
print(s)
async def forever(s):
while True:
await aprint(s)
async def main():
await asyncio.gather(forever('a'), forever('b'))
asyncio.run(main())
据我了解,会发生以下情况:
- asyncio.run(main()) 对事件循环进行任何必要的全局初始化并安排 main() 执行。
- main() 安排 asyncio.gather(...) 执行并等待其结果
- asyncio.gather 调度 forever('a') 和 forever('b') 的执行
- 无论哪一个先执行,它们都会立即等待 aprint() 并让调度程序有机会在需要时运行另一个协程(例如,如果我们从 'a' 开始,那么我们就有机会开始尝试评估 'b' ,它应该已经被安排执行)。
- 在输出中,我们将看到一行行,每行都包含“a”或“b”,并且调度程序应该足够公平,以便我们在足够长的时间段内至少看到每个行。李>
实际上,这不是我观察到的。相反,整个程序相当于while True: print('a')。我发现非常有趣的是,即使是对代码的微小更改似乎也重新引入了公平性。例如,如果我们改为使用以下代码,那么我们会在输出中得到大致相等的 'a' 和 'b' 组合。
async def forever(s):
while True:
await aprint(s)
await asyncio.sleep(1.)
验证它似乎与我们在无限循环中花费多长时间没有任何关系,我发现以下更改也提供了公平性。
async def forever(s):
while True:
await aprint(s)
await asyncio.sleep(0.)
有谁知道为什么会发生这种不公平以及如何避免这种情况?我想当有疑问时,我可以主动在任何地方添加一个空的 sleep 语句,并希望就足够了,但是对于我来说,为什么原始代码没有按预期运行是非常不明显的。
如果 asyncio 似乎经历了很多 API 更改,那么我在 Ubuntu 机器上使用 Python 3.8.4 的香草安装。
【问题讨论】:
-
这能回答你的问题吗? How does asyncio actually work?
-
@MisterMiyagi 是的,谢谢。当您确切知道要搜索什么时,此站点上的所有内容都是重复的,不是吗;)
-
只是推荐一些重复的——它们实际上是作为建议,而不是作为欺骗锤。 ;) 随意选择您认为合适的内容。
-
哦,抱歉,很明显您没有分配欺骗锤(尤其是没有关闭标志)。我还评论了如何知道在哪里寻找和搜索什么可以成为整个战斗,我非常感谢这些链接。
标签: python python-asyncio