【问题标题】:asyncio gather scheduling order guaranteeasyncio 收集调度顺序保证
【发布时间】:2019-02-13 11:05:17
【问题描述】:

是否保证作为asyncio.gather 参数的协程将 安排保留订单?考虑以下示例:

import asyncio

async def coro(i):
    print('{i} finished'.format(i=i))

async def main():
    await asyncio.gather(
            coro(0),
            coro(1),
            coro(2),
            coro(3),
            coro(4),
    )

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

结果:

0 finished
1 finished
2 finished
3 finished
4 finished

【问题讨论】:

  • 绝对不能保证它们会按特定顺序安排(或执行)。 保证的是传递给gather的等待对象的结果将与对应等待对象的顺序相同。

标签: python python-asyncio


【解决方案1】:

From Python documentation

awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

同时运行 aws 序列中的等待对象。

如果aws中的任何awaitable是协程,它会被自动调度 作为一个任务。

如果所有 awaitables 都成功完成,结果是 返回值的聚合列表。 结果值的顺序 对应于 aws 中 awaitable 的顺序。

所以结果值的顺序是保留的,但执行顺序不是。

【讨论】:

  • @VlanBezden 上面 OP 粘贴的输出来自协程中的 print。所以上面的输出显示的是执行顺序,而不是asyncio.gather收集的顺序。我通过在print 之前添加asyncio.sleep(1) 来尝试上述代码。 Py3.7 总是按顺序打印它们,而 Py3.6 的输出总是不同的。 Py3.7 中是否有至少按顺序开始执行的东西,而 Py3.6 中没有?
【解决方案2】:

是的,至少从cpython实现的源代码来看,它们会按顺序调度。协同例程列表被迭代,并为每个协同例程一个一个地添加到循环中。虽然我没有看到调度保证在哪种情况下变得重要。不能保证循环将按顺序执行它们,也不能保证它们将按该顺序完成。我认为这将取决于特定循环的实现细节和代码的性质。例如,尝试在打印之前将 asyncio.sleep(1) 添加到协程中。

【讨论】:

  • 我在打印之前添加了asyncio.sleep(1),并多次运行了 OP 的代码。在 Py3.7 中,输出顺序始终与 asyncio.gather 调用中的相同。在 Py3.6 中,它们总是不同的。这让我觉得 Py3.7 中是否有一些东西至少可以按顺序开始执行? py3.7 中的每次运行都会按顺序打印输出,而 3.6 中的每次运行都会产生不同的输出,这绝非巧合。
【解决方案3】:

至少在 Python 3.6 中,有一个有趣的极端情况,即顺序崩溃。

我们举个这样的例子:

import asyncio

async def main():
    data = [1, 2, 3, 4, 5, 6, 7, 8]

    async def aiter(iterable):
        for v in iterable:
            yield v

    aiterable = aiter(data)
    aiterables = [aiterable] * 8

    values = await asyncio.gather(
        *[it.__anext__() for it in aiterables])
    
    assert values == data, f'{values} != {data}'

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Py36 中的近似结果:

AssertionError: [7, 6, 3, 4, 5, 2, 8, 1] != [1, 2, 3, 4, 5, 6, 7, 8]

这种行为看起来合乎逻辑,因为__anext__() 协程以任意顺序执行。但是,在 Py37+ 中,代码运行良好,并且输出列表顺序得以保留。我在文档中没有找到任何提及这一点。

【讨论】:

    猜你喜欢
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-12
    • 2010-10-23
    相关资源
    最近更新 更多