加载中...

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)

注意事项

  1. 必须加信号量或锁,否则多线程并发会导致竞态条件,最终 x 可能小于 500(如 300、400)。

  2. executor.shutdown(wait=True) 等待任务完成后再print(x),否则print(x) 可能小于500

posted @ 2025-06-18 19:16  最大的敌人是自律  阅读(210)  评论(0)    收藏  举报