Python中的loop.run_in_executor基本用法

 更新时间:2025年12月15日 11:42:57   作者:无风听海  
文章介绍了在Python的异步编程中使用`loop.run_in_executor`来执行阻塞操作的方法,包括基本用法、线程池和进程池的选择、与协程的差异对比、进阶用法、注意事项以及真实场景举例,感兴趣的朋友跟随小编一起看看吧

✅ 一、背景:为什么需要loop.run_in_executor

在 Python 的异步编程(asyncio)中,协程可以并发运行,提高效率,但它们依赖于“非阻塞的 I/O”。如果你在协程中调用了一个阻塞的操作(比如 time.sleep()requests.get() 等),它会阻塞整个事件循环,导致其他协程也无法继续执行

为了解决这个问题,Python 提供了 loop.run_in_executor(),允许你把阻塞的同步代码“放在后台线程或进程中执行”,从而不影响主事件循环。

🧪 二、基本用法和执行流程

✅ 基本结构:

loop.run_in_executor(executor, func, *args)
  • executor: 指定使用的执行器(线程池或进程池),为 None 时使用默认线程池。
  • func: 要执行的同步阻塞函数。
  • *args: 传递给函数的参数。

✅ 最小示例(阻塞函数放到线程中):

import asyncio
import time
def blocking_func(name):
    print(f"开始阻塞任务 {name}")
    time.sleep(3)
    print(f"结束阻塞任务 {name}")
    return f"{name} done"
async def main():
    loop = asyncio.get_running_loop()
    # 把阻塞函数丢进默认线程池
    result = await loop.run_in_executor(None, blocking_func, "任务A")
    print(result)
asyncio.run(main())

⏳ 输出:

开始阻塞任务 任务A
结束阻塞任务 任务A
任务A done

注意:虽然 blocking_func() 是阻塞的,但不会阻塞 asyncio 主事件循环,因此你可以同时运行其他协程。

🧵 三、线程池 vs 进程池

1. ThreadPoolExecutor(默认)

  • 用于 I/O 密集型任务(网络请求、文件 I/O 等)
  • 启动快,线程共享内存,效率高
  • run_in_executor(None, ...) 就是使用默认线程池

2. ProcessPoolExecutor

  • 用于 CPU 密集型任务(图像处理、数据加密、科学计算等)
  • 每个进程独立内存,更耗资源,但避免 GIL 限制
  • 用于充分利用多核 CPU

示例(使用 ProcessPoolExecutor):

from concurrent.futures import ProcessPoolExecutor
def compute(n):
    return sum(i * i for i in range(n))
async def main():
    loop = asyncio.get_running_loop()
    with ProcessPoolExecutor() as executor:
        result = await loop.run_in_executor(executor, compute, 10_000_000)
        print(result)
asyncio.run(main())

📊 四、run_in_executor 与协程的差异对比

特性协程 (async def)run_in_executor
是否阻塞事件循环
适用于异步 I/O 操作同步阻塞操作
是否需要线程或进程是(线程或进程池)
是否自动并发
是否可中断可以使用 asyncio.CancelledError线程执行不能中断

🔁 五、与其他异步写法对比

❌ 错误做法(会阻塞整个事件循环):

import asyncio
import time
async def wrong():
    time.sleep(2)  # 阻塞整个事件循环!
    print("完成")
asyncio.run(wrong())

✅ 正确做法(使用run_in_executor):

async def correct():
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(None, time.sleep, 2)
    print("完成")

🔐 六、进阶用法:并行多个任务

import asyncio
import time
def task(name, duration):
    print(f"开始 {name}")
    time.sleep(duration)
    print(f"结束 {name}")
    return name
async def main():
    loop = asyncio.get_running_loop()
    tasks = [
        loop.run_in_executor(None, task, 'A', 2),
        loop.run_in_executor(None, task, 'B', 3),
        loop.run_in_executor(None, task, 'C', 1),
    ]
    results = await asyncio.gather(*tasks)
    print("全部完成:", results)
asyncio.run(main())

⏳ 输出(并行):

开始 A
开始 B
开始 C
结束 C
结束 A
结束 B
全部完成: ['A', 'B', 'C']

⚠️ 七、注意事项与潜在陷阱

问题描述与解决
共享资源问题多线程操作同一个变量可能会出错,考虑加锁或使用 asyncio.Queue
线程池大小限制默认线程池大小有限(通常为 CPU 核心数的 5 倍),可手动调整
异常处理在线程中抛出的异常必须在主线程中 await 时捕获
进程池不能用 lambda进程池中的函数必须是可序列化的,不能是匿名函数或本地函数
不能中断线程任务一旦 run_in_executor 开始执行函数,无法强制中断线程任务

💼 八、真实场景举例

示例 1:读取大文件(I/O 密集型)

def read_file(path):
    with open(path, 'r') as f:
        return f.read()
data = await loop.run_in_executor(None, read_file, "bigfile.txt")

示例 2:调用同步网络库(如requests)

import requests
def fetch(url):
    response = requests.get(url)
    return response.text
html = await loop.run_in_executor(None, fetch, "https://example.com")

实际建议:使用 httpx.AsyncClient 替代 requests

🧩 九、封装通用工具函数(推荐写法)

import asyncio
from typing import Callable, Any
from functools import partial
async def to_thread(func: Callable, *args, **kwargs) -> Any:
    loop = asyncio.get_running_loop()
    return await loop.run_in_executor(None, partial(func, *args, **kwargs))
# 使用方式
result = await to_thread(my_blocking_function, arg1, arg2)

✅ 十、Python 3.9+ 新特性:asyncio.to_thread

从 Python 3.9 起,你可以直接用内置的 asyncio.to_thread() 来代替 run_in_executor(None, ...),更加简洁:

import asyncio
def blocking_func():
    ...
await asyncio.to_thread(blocking_func)

等价于:

await loop.run_in_executor(None, blocking_func)

📘 总结

特点内容
功能异步执行阻塞的同步函数
用法await loop.run_in_executor(executor, func, *args)
默认线程池executor=None
用于场景文件 I/O、网络请求、同步数据库操作、CPU 密集计算
替代方案Python 3.9+: asyncio.to_thread()

到此这篇关于Python中的loop.run_in_executor基本用法的文章就介绍到这了,更多相关Python loop.run_in_executor内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • python如何实现word批量转HTML

    python如何实现word批量转HTML

    这篇文章主要介绍了python如何实现word批量转HTML,帮助大家更好的理解和学习python,感兴趣的朋友可以了解下
    2020-09-09
  • python使用cStringIO实现临时内存文件访问的方法

    python使用cStringIO实现临时内存文件访问的方法

    这篇文章主要介绍了python使用cStringIO实现临时内存文件访问的方法,涉及Python使用cStringIO模块操作内存的技巧,需要的朋友可以参考下
    2015-03-03
  • Python Matplotlib绘制动画的代码详解

    Python Matplotlib绘制动画的代码详解

    使用matplotlib可以很容易地创建动画框架。在本文中我们就将利用Matplotlib制作几个简单的动画,文中的示例代码讲讲详细,感兴趣的可以了解下
    2022-05-05
  • Google colab中从kaggle中接入数据的操作方法

    Google colab中从kaggle中接入数据的操作方法

    这篇文章主要介绍了Google colab中如何从kaggle中接入数据,本文涉及到两大平台内容,所以我默认你已经拥有了,并且使用过了一段时间的google账号和kaggle账号,需要的朋友可以参考下
    2024-03-03
  • 使用Python实现无损放大图片功能

    使用Python实现无损放大图片功能

    本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先转换为RGB,PNG格式则更适合无损放大,需要的朋友可以参考下
    2025-08-08
  • python脚本框架webpy入门安装及应用创建

    python脚本框架webpy入门安装及应用创建

    这篇文章主要为大家介绍了python脚本框架web.py的入门安装及应用创建过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-11-11
  • 浅谈Python如何获取excel数据

    浅谈Python如何获取excel数据

    这篇文章主要介绍了Python如何获取excel数据,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • windows 下python+numpy安装实用教程

    windows 下python+numpy安装实用教程

    这篇文章主要介绍了windows 下python+numpy安装实用教程,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • 使用Python中OpenCV和深度学习进行全面嵌套边缘检测

    使用Python中OpenCV和深度学习进行全面嵌套边缘检测

    这篇文章主要介绍了使用Python中OpenCV和深度学习进行全面嵌套边缘检测,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • 解决pandas使用read_csv()读取文件遇到的问题

    解决pandas使用read_csv()读取文件遇到的问题

    今天小编就为大家分享一篇解决pandas使用read_csv()读取文件遇到的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06

最新评论