一看就会的python线程池

前言

实习生:为什么每次让写多线程任务,我总是暗自恐慌,然后去网上搜索相关的代码?
我:你就没有真正搞清楚应该怎么写,也并没有真正的手动写过,所以你心里没底就会慌乱。
实习生:那你能不能告诉我一种让我很快掌握,甚至能一次性记住一种写处理多线程代码模板。
我:你可以直接用线程池。

极简案例

代码示例
import time
from concurrent.futures import ThreadPoolExecutor

# 模拟 I/O 密集型任务:等待 n 秒后返回结果
def process_task(task_id):
    print(f"正在处理任务:{task_id}")
    time.sleep(1)  # 模拟 I/O 阻塞(如网络请求、文件读取)
    print(f"任务 {task_id} 处理完成\n")

def run_threadpool():
    print("【线程池并发】开始...")
    start = time.time()
    with ThreadPoolExecutor(max_workers=5) as executor:
        # 提交所有任务
        [executor.submit(process_task, i + 1) for i in range(5)]
    end = time.time()
    print(f"✅ 线程池总耗时: {end - start:.2f} 秒\n")
    
if __name__ == "__main__":
    run_threadpool()
运行结果
【线程池并发】开始...
正在处理任务:1
正在处理任务:2
正在处理任务:3
正在处理任务:4
正在处理任务:5
任务 3 处理完成
任务 4 处理完成
任务 2 处理完成
任务 5 处理完成
任务 1 处理完成

✅ 线程池总耗时: 1.01 秒
示例讲解
  1. 可以看到要使用线程池只需要两行代码:
# 创建线程池
with ThreadPoolExecutor(max_workers=5) as executor:
    # 用submit提交任务
    [executor.submit(process_task, i + 1) for i in range(5)]
  • submit第一个参数就是要执行的方法名称(不带括号)
  • submit第二个参数就是刚方法的参数
  1. 可以看到方法中休眠1s,如果同步执行显然的5s,但是开启5个线程,处理5个任务,依然只需要1s。

怎么样?简单吧,你现在已经学会了如何使用线程池处理多线程任务了。

进一步丰富线程池的使用

我们看看上面极简案例,会发现一些“小”问题:

如果方法有返回值,我们并没有拿到该返回值

获取任务返回值示例
import time
from concurrent.futures import ThreadPoolExecutor

# 模拟 I/O 密集型任务:等待 n 秒后返回结果
def process_task(task_id):
    time.sleep(1)  # 模拟 I/O 阻塞(如网络请求、文件读取)
    return f"任务 {task_id} 完成"

def run_threadpool():
    print("【线程池并发】开始...")
    start = time.time()
    with ThreadPoolExecutor(max_workers=5) as executor:
        # 先 submit 所有任务,得到 Future 列表
        tasks = [2, 3, 1, 4, 5]
        futures = [executor.submit(process_task, i) for i in tasks]
        results = [f.result() for f in futures]  # 按提交顺序获取
        for res in results:
            print(res)
    end = time.time()
    print(f"✅ 线程池总耗时: {end - start:.2f} 秒\n")
    
if __name__ == "__main__":
    run_threadpool()
运行结果
【线程池并发】开始...
任务 2 完成
任务 3 完成
任务 1 完成
任务 4 完成
示例讲解
  1. 我们把提交线程池后的结果返回,是一个Future 列表
futures = [executor.submit(process_task, i) for i in tasks]
  1. 使用future的result方法就可以获取到任务的返回值
results = [f.result() for f in futures]
  1. 我们可以看到上面返回的顺序是和任务输入的顺序的一致的。

原理就是输出是一个“有序队列”,必须按序“放行”结果。

这在很关心顺序,但对于效率不是很关心的场景很有用,比如生成报表、按行处理 CSV等等。

map方法代替submit+列表推导式

上面的代码用map方法也可以实现:

import time
from concurrent.futures import ThreadPoolExecutor

# 模拟 I/O 密集型任务:等待 n 秒后返回结果
def process_task(task_id):
    time.sleep(1)  # 模拟 I/O 阻塞(如网络请求、文件读取)
    return f"任务 {task_id} 完成"

def run_threadpool():
    print("【线程池并发】开始...")
    start = time.time()
    with ThreadPoolExecutor(max_workers=5) as executor:
        # 使用map替代submit + 列表推导式
        tasks = [2, 3, 1, 4, 5]
        results = executor.map(process_task, tasks)
        for res in results:
            print(res)
    end = time.time()
    print(f"✅ 线程池总耗时: {end - start:.2f} 秒\n")
    
if __name__ == "__main__":
    run_threadpool()

map就等同于submit + 列表推导式,也就是下面代码是等价的:

results = executor.map(process_task, tasks)

等价于

futures = [executor.submit(process_task, i) for i in tasks]
results = [f.result() for f in futures]  # 按提交顺序获取

所以当需要输出顺序 = 输入顺序的时候,可以使用map来简化submit+列表推导式。

那既然有关心输出顺序的场景,肯定还有不关心输出顺序,更关心效率的场景,此时就可以用as_completed()。

as_completed(输出顺序 = 任务完成先后顺序)
  1. 代码示例
import time
from concurrent.futures import ThreadPoolExecutor,as_completed

# 模拟 I/O 密集型任务:等待 n 秒后返回结果
def process_task(task_id):
    time.sleep(1)  # 模拟 I/O 阻塞(如网络请求、文件读取)
    return f"任务 {task_id} 完成"

def run_threadpool():
    print("【线程池并发】开始...")
    start = time.time()
    with ThreadPoolExecutor(max_workers=5) as executor:
        tasks = [2, 3, 1, 4, 5]
        # 先 submit 所有任务,得到 Future 列表
        futures = [executor.submit(process_task, i) for i in tasks]
        # 谁先完成,就先处理谁
        for future in as_completed(futures):
            result = future.result()
            print(result)
    end = time.time()
    print(f"✅ 线程池总耗时: {end - start:.2f} 秒\n")
    
if __name__ == "__main__":
    run_threadpool()
  1. 运行结果:
【线程池并发】开始...
任务 4 完成
任务 1 完成
任务 3 完成
任务 5 完成
任务 2 完成
✅ 线程池总耗时: 1.02 秒
  1. 示例详解
    as_completed有以下特点:
  • 输出顺序 = 完成先后顺序
  • 可以立即响应已完成的任务(适合实时反馈)
  • 适合:不关心顺序,只关心尽快拿到结果的场景(如爬虫、批量 API 调用)
  1. 对比顺序输出代码区别
    其实两者区别很简单,就是一个直接遍历Future 列表,另一个是遍历as_completed(Future 列表),也就是加入as_completed方法后,将就会在任务完成后里面返回结果,不考虑任务输入顺序的问题,显然效率会更快。

参考文献

https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.ThreadPoolExecutor

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SunnyRivers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值