asyncio的默认调度程序何时生效?

问题描述

据我了解,asyncio.gather旨在同时运行其参数,并且当协程执行await表达式时,它为事件循环提供了安排其他任务的机会。考虑到这一点,我很惊讶地看到以下代码片段忽略了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())

据我了解,发生了以下事情:

  1. asyncio.run(main())对事件循环进行必要的全局初始化,并计划main()进行执行。
  2. main()调度asyncio.gather(...)以便执行并等待其结果
  3. asyncio.gather计划执行forever('a')和forever('b')
  4. 无论哪个首先执行,它们都会立即等待aprint()并为调度程序提供运行另一个协程的机会(例如,如果我们以“ a”开头,那么我们就有机会开始尝试评估“ b”) ,应该已经安排了执行时间。
  5. 在输出中,我们将看到一行包含'a'或'b'的行,并且调度程序应该足够公平,以便在足够长的时间内我们至少看到其中每一行。
  6. li>

实际上,这不是我观察到的。相反,整个程序等效于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的原始安装。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)