查看: 409|回复: 1

Python多线程入门实战:用ThreadPoolExecutor优化IO密集型任务

[复制链接]
发表于 昨天 16:00 | 显示全部楼层 |阅读模式
单线程程序在处理网络请求、文件IO或数据库查询时,CPU往往在等待结果而空转。多线程的核心价值就是让CPU在等待时去执行其他任务,从而提升整体效率。不过Python的全局解释器锁(GIL)决定了同一时刻只有一个线程能执行Python字节码,因此多线程最适合IO密集型任务(如网络请求、文件读写),而不适用于CPU密集型计算(如大量数学运算)。

了解GIL后,我们先从最基础的threading模块入手。以下示例演示如何创建两个线程并让它们并发执行:
  1. import threading
  2. import time
  3. def task(name, delay):
  4.     print(f"{name} 开始")
  5.     time.sleep(delay)
  6.     print(f"{name} 结束")
  7. t1 = threading.Thread(target=task, args=("任务A", 2))
  8. t2 = threading.Thread(target=task, args=("任务B", 1))
  9. t1.start()
  10. t2.start()
  11. t1.join()
  12. t2.join()
  13. print("全部完成")
复制代码

输出顺序显示两个任务几乎同时启动,总耗时约2秒(取最长的延迟),而非顺序执行的3秒。

实际开发中最常见的多线程场景是并发请求多个API接口。使用threading时需要手动管理线程列表,并通过Lock保护共享数据:
  1. import threading
  2. import requests
  3. urls = [
  4.     "https://api.example.com/1",
  5.     "https://api.example.com/2",
  6.     "https://api.example.com/3",
  7. ]
  8. results = []
  9. lock = threading.Lock()
  10. def fetch(url):
  11.     resp = requests.get(url, timeout=5)
  12.     with lock:
  13.         results.append(resp.status_code)
  14. threads = []
  15. for url in urls:
  16.     t = threading.Thread(target=fetch, args=(url,))
  17.     t.start()
  18.     threads.append(t)
  19. for t in threads:
  20.     t.join()
  21. print(f"完成 {len(results)} 个请求")
复制代码

然而手动管理线程较为繁琐,更推荐使用concurrent.futures.ThreadPoolExecutor。它内置线程池,自动管理线程的创建与回收,并提供了简洁的map方法:
  1. from concurrent.futures import ThreadPoolExecutor
  2. import requests
  3. urls = ["url1", "url2", "url3"]
  4. def fetch(url):
  5.     return requests.get(url).status_code
  6. with ThreadPoolExecutor(max_workers=5) as executor:
  7.     results = list(executor.map(fetch, urls))
  8. print(results)
复制代码

通过max_workers参数控制并发数,避免对目标服务器造成过大压力。

使用多线程时需注意三个常见陷阱:

1. GIL导致计算密集型任务不加速:如果线程内执行纯Python循环(如for i in range(10000000): pass),启动10个线程也不会比单线程快。此类场景应改用multiprocessing模块。

2. 共享数据竞争:多个线程同时修改同一个变量可能产生不可预期的结果。使用threading.Lock()加锁即可保证数据安全。

3. 线程数过多:每个线程都有内存和上下文切换开销,盲目开上千个线程反而降低性能。一般建议IO密集型任务的线程数设为CPU核数的2~5倍。

一句话总结:Python多线程的核心价值是让IO等待的时间不被浪费。只要牢记GIL的存在,避免将其用于计算密集型任务,多线程就能显著提升程序响应速度。
回复

使用道具 举报

发表于 昨天 16:10 | 显示全部楼层

Re: Python多线程入门实战:用ThreadPoolExecutor优化IO密集型任务

这篇教程写得非常清晰实用!从手动管理线程到ThreadPoolExecutor的演进,确实能帮新手少走很多弯路。特别是GIL的提醒和三个常见陷阱,都是实际开发中容易踩的坑。我之前用多线程爬数据时就因为线程数设太大导致目标服务器限流,后来才意识到要根据场景调整max_workers。想请教下,如果任务既有IO等待又有少量计算(比如解析JSON),是否也适合用多线程?还是说应该考虑多进程+多线程组合?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-14 02:02 , Processed in 0.023734 second(s), 17 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部