运行 IOLoop.current() 中的函数。run_in_executor?

Running function in IOLoop.current().run_in_executor?

我看了下面的代码

async def f():
    sc_client = session.client("ec2")
    for id in ids:
        await IOLoop.current().run_in_executor(None, lambda: client.terminate(id))

它与以下代码相比如何? client.terminate 会是 运行 并行吗?但是每次执行都在等待?

    for id in ids:
        client.terminate(id)

Will client.terminate be run in parallel?

不,它仍然是 运行 顺序。

IOLoop.current().run_in_executor 将 运行 blocking function 在一个单独的线程中 returns 一个 asyncio.Future,而 await 将等到 Future 调用 client.terminate 完成,然后循环继续。

你给出的两个选项的区别是:

如果程序有其他协程到运行,使用第一个选项,另一个协程不会阻塞,而使用第二个选项,另一个协程将阻塞等待你的for循环完成。

举个例子让大家明白(这里为了简单起见,会用loop.run_in_executor来模拟IOLoop.current().run_in_executor):

test.py:

import asyncio
import concurrent.futures
import time

def client_terminate(id):
    print(f"start terminate {id}")
    time.sleep(5)
    print(f"end terminate {id}")

async def f():
    loop = asyncio.get_running_loop()

    for id in range(2):
        with concurrent.futures.ThreadPoolExecutor() as pool:
            await loop.run_in_executor(pool, client_terminate, id)
        # client_terminate(id)

async def f2():
    await asyncio.sleep(1)
    print("other task")

async def main():
    await asyncio.gather(*[f(), f2()])

asyncio.run(main())

运行输出为:

$ python3 test.py
start terminate 0
other task
end terminate 0
start terminate 1
end terminate 1

你可以看到 for 循环中的 two client_terminate 仍然按顺序 运行s,BUT,函数 f2 打印 other task 在它们之间注入,它不会阻塞 asyncio scheduler 调度 f2.

补充:

如果将await loop.run_in_executor & threadpool相关的2行注释掉,直接调用client_terminate(id),输出为:

$ python3 test.py
start terminate 0
end terminate 0
start terminate 1
end terminate 1
other task

意味着如果你不将 blocking function 包裹在 Future 中,other task 将不得不等待你的 for loop 完成,这会浪费 CPU.