苦练 Python 第 68 天:并发狂飙!concurrent 模块让你 CPU 原地起飞

前言

大家好,我是 倔强青铜三 。欢迎关注我,微信公众号: 倔强青铜三。点赞、收藏、关注,一键三连!

今天咱们来一起挖掘 Python 自带的 "并发神器" ------ concurrent 模块,让你的 CPU 性能瞬间拉满。

一、concurrent 模块登场的必要性

Python 中已经有了 threading 和 multiprocessing 这两个模块来处理多线程和多进程相关操作,但是 concurrent 模块的出现有其独特的优势:

  • 高层抽象,简化开发 :它在 threading 和 multiprocessing 基础之上提供了更高层次的抽象,使得开发者可以更加方便地构建并行程序,无需过多关注底层细节。
  • 统一接口,便于切换 :通过 concurrent.futures 模块中的 Executor 接口,无论是使用线程池还是进程池,都有一致的接口和使用方式,方便在不同场景下灵活切换。
  • 强大功能,提升效率 :提供了诸如 Future 对象等强大的功能,方便对异步操作的结果进行管理和监控,有效提升并发编程的效率和可维护性。

concurrent 模块常用 API 一览表

API 作用 关键入参 返回值
concurrent.futures.ThreadPoolExecutor 创建线程池执行器 max_workers 最大工作线程数等 Future 对象
concurrent.futures.ProcessPoolExecutor 创建进程池执行器 max_workers 最大工作进程数等 Future 对象
concurrent.futures.Future 表示异步执行的操作 异步操作的结果

二、快速体验 ThreadPoolExecutor 并发下载

python 复制代码
# demo_thread_pool.py
import concurrent.futures
import urllib.request
import time

# 模拟下载任务
def download_url(url):
    try:
        with urllib.request.urlopen(url, timeout=5) as response:
            content = response.read()
            return len(content)
    except Exception as e:
        return str(e)

urls = [
    "https://www.baidu.com",
    "https://www.qq.com",
    "https://www.taobao.com",
    "https://www.jd.com",
    "https://www.sina.com.cn"
]

# 单线程下载
start_time = time.time()
for url in urls:
    print(download_url(url))
print("单线程下载耗时:", time.time() - start_time)

# 线程池下载
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    start_time = time.time()
    futures = {executor.submit(download_url, url): url for url in urls}
    for future in concurrent.futures.as_completed(futures):
        url = futures[future]
        try:
            result = future.result()
            print(f"{url}: {result}")
        except Exception as e:
            print(f"{url} 生成了异常: {e}")
    print("线程池下载耗时:", time.time() - start_time)

运行效果示例(因网络等因素实际结果可能不同):

arduino 复制代码
单线程下载耗时: 0.4107840061187744
https://www.baidu.com: 29506
https://www.taobao.com: 83249
https://www.qq.com: 122185
https://www.jd.com: 177956
https://www.sina.com.cn: 397807
线程池下载耗时: 0.136491060256958

三、ProcessPoolExecutor 处理 CPU 密集型任务

python 复制代码
# demo_process_pool.py
import concurrent.futures
import time

# CPU 密集型任务
def cpu_intensive_task(x):
    result = 0
    for i in range(10**7):
        result += i ** x
    return result

data = [2, 3, 4, 5, 6, 7, 8, 9]

# 单进程处理
start_time = time.time()
for num in data:
    print(cpu_intensive_task(num))
print("单进程处理耗时:", time.time() - start_time)

# 进程池处理
with concurrent.futures.ProcessPoolExecutor() as executor:
    start_time = time.time()
    futures = {executor.submit(cpu_intensive_task, num): num for num in data}
    for future in concurrent.futures.as_completed(futures):
        num = futures[future]
        try:
            result = future.result()
            print(f"任务 {num} 的结果:{result}")
        except Exception as e:
            print(f"任务 {num} 生成了异常: {e}")
    print("进程池处理耗时:", time.time() - start_time)

运行效果示例(具体数值和耗时根据机器性能而定):

erlang 复制代码
单进程处理耗时: 45.6789
任务 2 的结果: 333333330000000
任务 3 的结果: 4000000060000000
...
进程池处理耗时: 12.3456

四、Future 对象掌控异步操作

python 复制代码
# demo_future.py
import concurrent.futures
import time

def task(n):
    time.sleep(n)
    return f"任务完成,休眠了 {n} 秒"

# 创建线程池执行器
executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)

# 提交任务并获取 Future 对象
future1 = executor.submit(task, 2)
future2 = executor.submit(task, 3)

print("检查 task1 是否完成:", future1.done())  # False
print("检查 task2 是否完成:", future2.done())  # False

time.sleep(3)

print("检查 task1 是否完成:", future1.done())  # True
print("检查 task2 是否完成:", future2.done())  # True

print("task1 的结果:", future1.result())  # 获取任务结果
print("task2 的结果:", future2.result())

executor.shutdown()  # 关闭执行器

运行效果:

python 复制代码
检查 task1 是否完成: False
检查 task2 是否完成: False
检查 task1 是否完成: True
检查 task2 是否完成: True
task1 的结果: 任务完成,休眠了 2 秒
task2 的结果: 任务完成,休眠了 3 秒

五、如何选择合适的并发工具

任务类型判断

如果是 I/O 密集型任务,比如大量网络请求、文件操作等,使用 ThreadPoolExecutor 更合适。

因为它可以充分利用线程在等待 I/O 操作期间切换到其他任务执行的优势,提高整体效率。

而如果是 CPU 密集型任务,如复杂的数学计算、数据处理等,则更适合使用 ProcessPoolExecutor

因为多进程可以突破 Python 的全局解释器锁(GIL)限制,真正实现多个 CPU 核心并行计算。

资源限制考虑

根据系统的线程和进程资源限制,合理设置 max_workers 参数。

线程太多可能会导致频繁的线程切换开销,进程太多则会占用过多的内存和系统资源。

结果处理需求

如果需要对异步操作的结果进行复杂的处理和监控,Future 对象提供了丰富的接口和方法,可以方便地获取任务状态、结果、异常等信息。

无论使用线程池还是进程池,都可以借助 Future 来更好地管理和协调并发任务。


小结

武器 用途 一句话记忆
ThreadPoolExecutor 处理 I/O 密集型任务 I/O 密集型,线程池更高效
ProcessPoolExecutor 处理 CPU 密集型任务 CPU 密集型,进程池突破 GIL
Future 管理异步操作 异步操作好帮手
零依赖 官方自带 开箱即用

最后感谢阅读!欢迎关注我,微信公众号: 倔强青铜三
点赞、收藏、关注 ,一键三连!!

欢迎 点击 👍喜欢作者 按钮进行 打赏💰💰,请我喝一杯咖啡☕️!

相关推荐
星期天要睡觉4 小时前
深度学习——循环神经网络(RNN)实战项目:基于PyTorch的文本情感分析
人工智能·python·rnn·深度学习·神经网络
2401_858869804 小时前
目标检测2
人工智能·目标检测·计算机视觉
ARM+FPGA+AI工业主板定制专家4 小时前
基于ZYNQ的目标检测算法硬件加速器优化设计
人工智能·目标检测·计算机视觉·fpga开发·自动驾驶
koo3644 小时前
李宏毅机器学习笔记21-26周汇总
人工智能·笔记·机器学习
ERROR_LESS5 小时前
【ADS-1】【python基础-2】基本语法与数据结构(列表、字典、集合)
python
2401_841495645 小时前
【数据结构】基于Floyd算法的最短路径求解
java·数据结构·c++·python·算法··floyd
一晌小贪欢5 小时前
Python爬虫第6课:Selenium自动化浏览器与动态内容抓取
爬虫·python·selenium·网络爬虫·python基础·python3·pathon爬虫
格林威5 小时前
UV紫外相机的简单介绍和场景应用
人工智能·数码相机·计算机视觉·视觉检测·制造·uv·工业相机