我的理解是asyncio.gather
旨在运行其参数同时此外,当协程执行等待表达式时,它为事件循环提供了安排其他任务的机会。考虑到这一点,我惊讶地发现以下代码片段忽略了其中一个输入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.)
有谁知道为什么会发生这种不公平现象以及如何避免它?我想,当有疑问时,我可以主动在各处添加一个空的睡眠语句,并希望这足够了,但对我来说,为什么原始代码的行为不符合预期,这对我来说非常不明显。
以防万一,因为 asyncio 似乎已经经历了相当多的 API 更改,我在 Ubuntu 机器上使用 Python 3.8.4 的普通安装。
- 无论哪一个先执行,它们都会立即执行
await aprint()
如果需要的话,让调度程序有机会运行另一个协程
这部分是一个常见的误解。蟒蛇的await
并不意味着“将控制权交给事件循环”,它意味着“开始执行等待,allowing它让我们和它一起暂停”。所以是的,if等待的对象选择挂起,当前的协程也会挂起,等待它的协程也会挂起,依此类推,一直到事件循环。但如果等待的对象doesn't选择暂停,就像这样aprint
,等待它的协程也不会。正如所见,这有时是错误的来源here https://stackoverflow.com/a/48816319/1600898 or here https://stackoverflow.com/a/53377822/1600898.
有谁知道为什么会发生这种不公平现象以及如何避免它?
幸运的是,这种效果在不真正与外界交流的玩具示例中最为明显。虽然你可以通过添加来修复它们await asyncio.sleep(0)
到战略位置(甚至被记录为强制上下文切换),您可能不应该 https://stackoverflow.com/a/63437227/1600898在生产代码中执行此操作。
真正的程序将依赖于来自外部世界的输入,无论是来自网络、本地数据库的数据,还是来自由另一个线程或进程填充的工作队列的数据。实际数据很少会如此快地到达而导致程序的其余部分陷入饥饿,如果确实如此,饥饿可能是暂时的,因为程序最终会由于其自身的反压而挂起。output边。在极少数情况下,程序从一个源接收数据的速度比处理数据的速度快,但仍然需要观察来自另一个源的数据,您可能会遇到饥饿问题,但这可以通过强制上下文切换来解决if它曾经被证明会发生。 (我还没有听说有人在生产中遇到过它。)
除了上面提到的错误之外,更常见的情况是协程调用 CPU 密集型或遗留的阻塞代码,最终会占用事件循环。这种情况应该通过将 CPU/阻塞部分传递给run_in_executor https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)