python多线程ThreadPoolExecutor
多线程
含义
多线程是指在一个进程中开启多个执行线程。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。多线程允许程序在同一时间内执行多个任务,这些任务共享进程的资源,如内存空间、文件句柄等。
资源占用
线程共享进程的资源,创建和销毁线程的开销相对较小,但线程数量过多时,会导致线程切换开销增大,且多个线程竞争共享资源可能会引发同步问题。
并发方式
线程的调度由操作系统负责,是抢占式调度。多个线程可以在多个CPU核心上并行执行(如果系统支持多核),也可以在单核上通过时间片轮转的方式并发执行。
应用场景
适用于I/O密集型任务,如网络请求、文件读写等。因为在进行I/O操作时,线程可以释放CPU资源,让其他线程执行,提高CPU利用率
Python 多线程 ThreadPoolExecutor
ThreadPoolExecutor 是 Python concurrent.futures 模块提供的线程池实现,用于 并发执行多个 I/O 密集型任务,避免频繁创建和销毁线程的开销。
1. 基本用法
(1)创建线程池
from concurrent.futures import ThreadPoolExecutor
#创建线程池,max_workers 指定最大线程数(默认cup 核心数 * 5)
executor = ThreadPoolExecutor(max_workers=5)
(2) 提交任务
submit(fn, *args, **kwargs):提交单个任务,返回 Future 对象, 通过future.result() 返回结果
map(fn, *iterables):批量提交任务,类似 map() 函数 返回的是 结果的列表
示例1: submit 提交单个任务
import time
def task(name):
print(f"任务 {name} 开始")
time.sleep(2) # 模拟 I/O 操作
print(f"任务 {name} 结束")
return f"结果-{name}"
# 提交任务
future = executor.submit(task, "A")
print(future.result()) # 阻塞等待任务完成并获取结果
示例 2:map 批量提交任务
# 批量提交任务
results = executor.map(task, ["A", "B", "C"])
for result in results:
print(result) # 按任务顺序输出结果, 过程不一定是按顺序的
2. 获取任务结果
Future 对象提供以下方法:
-
result(timeout=None)`:阻塞等待任务完成并返回结果(可设置超时)
-
done()`:检查任务是否完成
-
add_done_callback(fn)`:任务完成后调用回调函数
示例:done() 和 add_done_callback
def callback(future):
print(f"任务完成,结果: {future.result()}")
future = executor.submit(task, "D")
future.add_done_callback(callback) # 任务完成后自动调用回调
while not future.done():
print("任务未完成,等待中...")
time.sleep(0.5)
3. 关闭线程池
-
shutdown(wait=True)`:关闭线程池
-
wait=True`:等待所有任务完成后再关闭
-
wait=False`:立即关闭,未完成的任务会被取消
示例
executor.shutdown(wait=True) # 等待所有任务完成
print("线程池已关闭")
futures.as_completed 详解
futures.as_completed 是 Python concurrent.futures 模块提供的一个函数,用于 按任务完成顺序迭代 Future 对象,而不是按提交顺序。它特别适合需要 尽快处理已完成任务 的场景,避免等待所有任务完成
1. 基本用法
(1) 导入 as_completed
from concurrent.futures import ThreadPoolExecutor, as_completed
(2) 提交多个任务并使用 as_completed 获取结果
def task(name):
import time
time.sleep(2) # 模拟 I/O 操作
return f"结果-{name}"
# 创建线程池
with ThreadPoolExecutor(max_workers=3) as executor:
# 提交多个任务,返回 Future 对象列表
futures = [executor.submit(task, f"任务{i}") for i in range(5)]
# 使用 as_completed 按完成顺序迭代 Future 对象
for future in as_completed(futures):
result = future.result() # 获取已完成任务的结果
print(result)
输出示例(任务可能以任意顺序完成):
复制结果-任务1
结果-任务0
结果-任务2
结果-任务3
结果-任务4
2. 核心特点
(1) 按完成顺序返回 Future 对象
不同于 executor.map() 或 executor.submit() + 列表推导(按提交顺序),as_completed 会 优先返回最早完成的任务,适合需要尽快处理结果的场景。
(2). 支持动态添加任务
可以在循环中动态提交新任务,并继续用 as_completed 监控所有任务(包括后续提交的)。
(3)超时控制
可以设置 timeout 参数,避免无限等待:
for future in as_completed(futures, timeout=10): # 最多等待 10 秒
result = future.result()
4. 对比其他方法
| 方法 | 特点 | 适用场景 |
|---|---|---|
executor.submit() + future.result() |
按提交顺序获取结果,需手动管理 Future 对象 |
需要精确控制每个任务的完成状态 |
executor.map() |
按提交顺序返回结果,代码简洁 | 任务数量固定且需顺序处理结果 |
as_completed() |
按完成顺序返回结果,支持动态任务 | 需要尽快处理已完成任务,或任务耗时差异大 |
5. 注意事项
(1)Future.result() 会阻塞
如果任务未完成,调用 future.result() 会阻塞当前线程,直到任务完成。
如果希望非阻塞,可以用 future.done() 先检查状态。
(2). 异常处理
如果任务抛出异常,future.result() 会重新抛出该异常。建议用 try-catch 捕获:
for future in as_completed(futures):
try:
result = future.result()
print(result)
except Exception as e:
print(f"任务失败: {e}")
示例
实现一个多线程程序,创建多个线程同时对一个共享变量进行累加操作,每个线程累加指定的次数, 比如 5个多线程,每个线程累加100次
from concurrent.futures import ThreadPoolExecutor
import threading
x = 0 #共享变量
executor = ThreadPoolExecutor(max_workers=5)
semaphore = threading.Semaphore(1) # 信号量,相当于互斥锁
def add_num(i, times):
global x
for _ in range(times):
semaphore.acquire() # 加锁
x = x + 1
semaphore.release() # 解锁
print(f'线程{i} 完成累加任务')
for i in range(5):
print(f"线程{i} 累加开始")
executor.submit(add_num, i, 100)
executor.shutdown(wait=True) # 等待所有任务完成
print(x)
注意事项
-
必须加信号量或锁,否则多线程并发会导致竞态条件,最终
x可能小于 500(如 300、400)。 -
executor.shutdown(wait=True) 等待任务完成后再print(x),否则print(x) 可能小于500

浙公网安备 33010602011771号