`asyncio` 循环中的饥饿

Starvation in `asyncio` loop

我有一个系统,其中两个“进程”AB 运行 在同一个 asyncio 事件循环中。

我注意到启动进程的顺序很重要 - 即如果我先启动进程 B 然后一直进程 B 运行s,而似乎 A 资源“匮乏”,反之亦然。

根据我的经验,这可能发生的唯一原因是 mutex 没有被 B 发布,但在下面的玩具示例中,它发生时没有任何 mutex ] 正在使用:

import asyncio


async def A():
    while True:
        print('A')
        await asyncio.sleep(2)


async def B():
    while True:
        print('B')
        await asyncio.sleep(8)


async def main():
    await B()
    await A()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

是在python进程不自动执行上下文切换?如果不是 - 我怎样才能让两个进程都参与其中,每个进程都在另一个进程空闲(即休眠)时参与?

TLDR:协程仅启用并发,它们不会自动触发并发。显式启动单独的任务,例如通过 create_taskgather,并发 运行 协程。

async def main():
    await asyncio.gather(B(), A())

asyncio 中的并发是通过任务处理的——与线程非常相似——它仅 组成 coroutines/awaitables——就像线程 functions/callables 组成。一般来说,一个coroutine/awaitable本身并不等同于一个单独的任务。

使用await X()表示“开始X等待它完成”。当按顺序使用多个这样的结构时:

async def main():
    await B()
    await A()

这意味着首先启动 B,只有在 B 完成后才启动 A:而 async defawait 允许并发 其他任务BA在单个任务中相对于彼此顺序运行。

添加并发的最简单方法是显式创建任务:

async def main():
    # execute B in a new task
    b_task = asyncio.create_task(B())
    # execute A in the current task
    await A()
    await b_task

请注意 B 如何卸载到新任务,同时仍然可以执行最终 await A() 以重新使用当前任务。

大多数异步框架都附带了用于常见并发场景的高级助手。在这种情况下,asyncio.gather 适合同时启动多个任务:

async def main():
    # execute B and A in new tasks
    await asyncio.gather(B(), A())