You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python异步同时运行多个阻塞函数?

问题描述

我是Python协程新手,使用Python 3.8.6,希望借助asyncio库并行处理阻塞函数。现有一个阻塞函数,需传入数组inputs中的不同参数并行执行:

示例耗时:

blocking_function(5) - takes 5 seconds
blocking_function(3) - takes 3 seconds
blocking_function(2) - takes 2 seconds

我尝试了以下代码,但函数仍串行执行:

inputs = [5,3,2]
async def main():
    tasks = [asyncio.create_task(blocking_function(input)) for input in inputs]
    result = await asyncio.gather(*tasks)
    print(result)

代码运行无报错,但总耗时8秒,如何实现并行执行使总耗时缩短至5秒?

解决方案

你的代码串行执行的核心原因是:blocking_function是阻塞函数,不会让出asyncio的事件循环。asyncio的协程并行依赖任务主动释放CPU(比如await异步操作),而阻塞函数会一直占用事件循环,导致其他任务无法执行。

要实现真正的并行,需要把阻塞函数放到线程池或进程池中运行,让asyncio在等待阻塞任务时可以切换到其他任务。

方法1:使用loop.run_in_executor(适配Python 3.8)

Python 3.9+的asyncio.to_thread本质是封装了run_in_executor,3.8可以直接用这个方法把阻塞函数放到线程中执行:

import asyncio
import time

# 模拟阻塞函数
def blocking_function(t):
    time.sleep(t)
    return f"Done in {t}s"

async def main():
    inputs = [5,3,2]
    loop = asyncio.get_event_loop()
    # 将每个阻塞任务提交到默认线程池
    tasks = [loop.run_in_executor(None, blocking_function, t) for t in inputs]
    results = await asyncio.gather(*tasks)
    print(results)

if __name__ == "__main__":
    start = time.time()
    asyncio.run(main())
    print(f"Total time: {time.time() - start:.2f}s")

这段代码会让每个blocking_function在独立线程中运行,总耗时约5秒,和最长任务的耗时一致。

方法2:显式指定线程池

如果需要控制线程数量,可以显式创建ThreadPoolExecutor:

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor

def blocking_function(t):
    time.sleep(t)
    return f"Done in {t}s"

async def main():
    inputs = [5,3,2]
    # 创建线程池,指定最大线程数
    with ThreadPoolExecutor(max_workers=3) as executor:
        loop = asyncio.get_event_loop()
        tasks = [loop.run_in_executor(executor, blocking_function, t) for t in inputs]
        results = await asyncio.gather(*tasks)
        print(results)

if __name__ == "__main__":
    start = time.time()
    asyncio.run(main())
    print(f"Total time: {time.time() - start:.2f}s")

额外说明

  • asyncio本身是单线程异步模型,无法直接并行执行阻塞代码,必须借助线程/进程池将阻塞任务移出事件循环线程。
  • 线程池适合IO密集型阻塞任务(如网络请求、文件读写);如果blocking_function是CPU密集型任务,建议改用ProcessPoolExecutor(进程池),避免GIL限制。

内容的提问来源于stack exchange,提问作者Shreyas Kulkarni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 00:00:07