Python使用多线程和异步调用
概述
在 Python 中,多线程和异步调用是处理并发任务的两种常用方式,适用于不同场景。
多线程(threading 模块)
多线程适合处理 I/O 密集型任务(如网络请求、文件读写),因为这类任务大部分时间在等待,线程可以在等待时切换到其他任务。
import threading
import time
def task(name, delay):
"""线程执行的任务"""
print(f"任务 {name} 开始")
time.sleep(delay) # 模拟I/O操作(如网络请求)
print(f"任务 {name} 完成")
# 创建线程
t1 = threading.Thread(target=task, args=("A", 2))
t2 = threading.Thread(target=task, args=("B", 3))
# 启动线程
t1.start()
t2.start()
# 等待所有线程完成
t1.join()
t2.join()
print("所有任务完成")
特点
- 优势:适合 I/O 密集型任务,线程切换开销小。
- 限制:受 GIL(全局解释器锁)影响,CPU 密集型任务(如大量计算)无法真正并行,多线程可能比单线程更慢。
- 适用场景:爬虫、API 调用、文件读写等。
线程池(ThreadPoolExecutor)
在 Python 中,线程池(ThreadPoolExecutor)是管理线程的高效方式,它可以预先创建一定数量的线程,避免频繁创建和销毁线程带来的开销,特别适合处理大量短期的 I/O 密集型任务(如网络请求、文件读写等)。
ThreadPoolExecutor 位于 concurrent.futures 模块中,使用起来非常简洁。
基本使用步骤
- 导入 ThreadPoolExecutor 类
- 创建线程池实例,指定最大线程数
- 提交任务到线程池(使用 submit() 或 map() 方法)
- 获取任务结果(可选)
- 关闭线程池(通常使用 with 语句自动管理)
示例 1:使用 submit() 提交单个任务
import time
from concurrent.futures import ThreadPoolExecutor
def task(task_id, sleep_time):
"""线程执行的任务:休眠指定时间后返回结果"""
print(f"任务 {task_id} 开始执行,将休眠 {sleep_time} 秒")
time.sleep(sleep_time) # 模拟I/O操作
result = f"任务 {task_id} 完成,休眠了 {sleep_time} 秒"
return result
def main():
# 创建线程池,最大线程数为3
with ThreadPoolExecutor(max_workers=3) as executor:
# 提交3个任务到线程池
futures = [
executor.submit(task, 1, 2), # 任务1,休眠2秒
executor.submit(task, 2, 3), # 任务2,休眠3秒
executor.submit(task, 3, 1) # 任务3,休眠1秒
]
# 获取所有任务的结果
for future in futures:
print(future.result()) # result()方法会阻塞直到任务完成
if __name__ == "__main__":
start_time = time.time()
main()
print(f"总耗时:{time.time() - start_time:.2f}秒")
运行结果说明:
- 3 个任务并发执行,总耗时接近最长任务的耗时(3 秒)
- with 语句会自动关闭线程池,无需手动调用 shutdown()
示例 2:使用 map() 批量处理任务
当需要对多个参数执行相同任务时,map() 方法更简洁:
import time
from concurrent.futures import ThreadPoolExecutor
def process_item(item):
"""处理单个项目的函数"""
item_id, sleep_time = item
print(f"处理项目 {item_id},休眠 {sleep_time} 秒")
time.sleep(sleep_time)
return f"项目 {item_id} 处理完成"
def main():
# 待处理的项目列表
items = [(1, 2), (2, 3), (3, 1), (4, 2)]
# 创建线程池,最大线程数为2
with ThreadPoolExecutor(max_workers=2) as executor:
# 使用map批量处理任务,返回结果顺序与输入顺序一致
results = executor.map(process_item, items)
# 遍历结果
for result in results:
print(result)
if __name__ == "__main__":
start_time = time.time()
main()
print(f"总耗时:{time.time() - start_time:.2f}秒")
map() 特点:
- 结果返回顺序与输入列表顺序一致
- 无需手动收集结果,直接迭代即可
示例 3:处理异常
线程池中的任务异常需要在获取结果时捕获:
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
def risky_task(task_id):
"""可能抛出异常的任务"""
print(f"任务 {task_id} 开始执行")
if task_id == 2:
raise ValueError(f"任务 {task_id} 发生错误")
time.sleep(1)
return f"任务 {task_id} 成功完成"
def main():
with ThreadPoolExecutor(max_workers=2) as executor:
futures = [executor.submit(risky_task, i) for i in range(1, 4)]
# 使用as_completed获取已完成的任务(顺序不确定)
for future in as_completed(futures):
try:
result = future.result()
print(result)
except ValueError as e:
print(f"捕获到异常:{e}")
if __name__ == "__main__":
main()
as_completed() 特点:
- 按任务完成顺序返回结果(与提交顺序可能不同)
- 便于实时处理已完成的任务
线程池关键参数与方法
1.max_workers:线程池最大线程数
- 对于 I/O 密集型任务,通常设置为 CPU核心数 * 5 左右
- 对于 CPU 密集型任务,建议设置为 CPU核心数 + 1
2.常用方法:
- submit(func, *args, **kwargs):提交单个任务,返回 Future 对象
- map(func, *iterables):批量提交任务,返回结果迭代器
- shutdown(wait=True):关闭线程池,wait=True 表示等待所有任务完成
3.Future 对象方法:
- result(timeout=None):获取任务结果,可设置超时时间
- add_done_callback(fn):任务完成后执行回调函数
- cancel():取消未执行的任务
适用场景
- 网络爬虫(批量请求网页)
- API 接口测试(并发调用多个接口)
- 日志处理(批量读取多个日志文件)
- 任何需要并发处理的 I/O 密集型任务
线程池避免了频繁创建线程的开销,同时通过限制最大线程数防止系统资源耗尽,是 Python 并发编程中非常实用的工具。
异步调用(asyncio 模块)
异步编程通过 事件循环 实现并发,更适合处理 高并发 I/O 任务(如大量网络请求),效率通常高于多线程。
import asyncio
import time
async def task(name, delay):
"""异步任务(必须用async定义)"""
print(f"任务 {name} 开始")
# 模拟I/O操作(必须用await调用异步函数)
await asyncio.sleep(delay)
print(f"任务 {name} 完成")
async def main():
# 创建任务列表
tasks = [
asyncio.create_task(task("A", 2)),
asyncio.create_task(task("B", 3))
]
# 等待所有任务完成
await asyncio.gather(*tasks)
# 启动事件循环
asyncio.run(main())
print("所有任务完成")
关键概念
- async:定义异步函数,函数返回值为协程(coroutine)。
- await:暂停当前协程,等待异步操作完成(只能在异步函数中使用)。
- 事件循环:管理所有协程的执行,决定何时切换任务。
特点
- 优势:纯 Python 层面实现并发,切换开销极小,适合超高并发 I/O 任务。
- 限制:需配合异步库(如 aiohttp 而非 requests),不适合 CPU 密集型任务。
- 适用场景:高并发 API 服务、WebSocket 服务、大量网络请求等。

浙公网安备 33010602011771号