让 asyncio 按顺序 运行 一个函数 (Python 3)

Getting asyncio to run a function in order (Python 3)

这里是一个使用 asyncio 打印出 0 到 9 数字的简单示例。

问题: 有时代码会打印出从 0 到 7 的数字,然后打印 9,然后打印 8。尤其是当您将 ThreadPoolExecutor 设置为较小的数字时,例如4 或 5。

0
1
2
3
4
5
6
7
9
8

如何让始终按从0到9的顺序打印?为什么没有按顺序打印?

0
1
2
3
4
5
6
7
8
9

代码

import asyncio
from concurrent.futures import ThreadPoolExecutor


async def printThreaded(THREAD_POOL, length):   
    loop = asyncio.get_event_loop()
    futures = []
    for i in range(length):
        futures.append(loop.run_in_executor(THREAD_POOL, echo, i))
    await asyncio.wait(futures)


def echo(i):
    print(i)


THREAD_POOL = ThreadPoolExecutor(16)
with THREAD_POOL:
    loop = asyncio.get_event_loop()
    length = 10
    loop.run_until_complete(printThreaded(THREAD_POOL, length))

现在您的代码中发生了什么?

您创建协程列表 (futures),每个 运行 echo 在线程池中,然后一次启动它们 (await asyncio.wait(futures))。由于同时有多个 echo 运行ning 并且每个打印都是 运行,所以所有这些打印都可以随时发生。

你想做什么?

你可能真的不想按顺序 运行 协同程序(否则你可以在没有 asyncio 的情况下循环调用它),你想要 运行 它们并发在线程池中,但按创建协程的顺序打印它们的结果

在这种情况下你应该:

  1. 将线程中发生的实际工作从 打印出来

  2. 可能更喜欢使用 asyncio.gather 来按顺序获得计算结果

  3. 终于打印出你在主线程中得到的有序结果

总结

这是上面解释过的代码的修改版本:

import time
from random import randint

import asyncio
from concurrent.futures import ThreadPoolExecutor


async def printThreaded(THREAD_POOL, length):   
    loop = asyncio.get_event_loop()

    # compute concurrently:
    coroutines = []
    for i in range(length):
        coroutine = loop.run_in_executor(THREAD_POOL, get_result, i)
        coroutines.append(coroutine)
    results = await asyncio.gather(*coroutines)

    # print ordered results:
    for res in results:
        print(res)



def get_result(i):

    time.sleep(randint(0, 2))  # actual work, reason you delegate 'get_result' function to threaed

    return i  # compute and return, but not print yet


THREAD_POOL = ThreadPoolExecutor(4)
with THREAD_POOL:
    loop = asyncio.get_event_loop()
    length = 10
    loop.run_until_complete(printThreaded(THREAD_POOL, length))