并发模型深度对比
处理高并发请求时,应该选择同步,多线程还是异步?本文从原理到实战,帮你做出正确选择.
一,三种模型原理对比
[同步模型] 请求1 → 处理 → 响应 请求2 → 处理 → 响应 (等请求1完成) 请求3 → 处理 → 响应 (等请求2完成) 特点:简单直观,但IO等待时CPU空转 [多线程模型] 线程1: 请求1 → 处理 → 响应 线程2: 请求2 → 处理 → 响应 (并行执行) 线程3: 请求3 → 处理 → 响应 (并行执行) 特点:真并行,但有线程切换开销和GIL限制 [异步模型] 事件循环: 请求1 → 开始处理 → IO等待 → 挂起 → IO完成 → 恢复 → 响应 请求2 → 开始处理 → IO等待 → 挂起 → IO完成 → 恢复 → 响应 请求3 → 开始处理 → IO等待 → 挂起 → IO完成 → 恢复 → 响应 特点:单线程处理多连接,IO等待时切换任务
二,代码实现对比
# [同步版本]简单但慢
import requests
def fetch_sync(urls):
'''同步获取多个URL'''
results = []
for url in urls:
# 阻塞等待,一个请求完成才能下一个
response = requests.get(url)
results.append(response.text)
return results
# 10个URL,每个1秒,总共10秒
urls = ["http://api.example.com/item/" + str(i) for i in range(10)]
results = fetch_sync(urls)
# [多线程版本]并行但有开销
import requests
from concurrent.futures import ThreadPoolExecutor
def fetch_threaded(urls, max_workers=5):
'''多线程并发获取'''
def fetch_one(url):
return requests.get(url).text
# 线程池管理线程生命周期
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# map自动分配任务到线程池
results = list(executor.map(fetch_one, urls))
return results
# 5个线程并发,约2秒完成
results = fetch_threaded(urls, max_workers=5)
# [异步版本]高效但代码复杂
import aiohttp
import asyncio
async def fetch_async(urls):
'''异步并发获取'''
async with aiohttp.ClientSession() as session:
# 创建所有协程任务
tasks = [fetch_one(session, url) for url in urls]
# 并发执行所有任务
results = await asyncio.gather(*tasks)
return results
async def fetch_one(session, url):
'''单个异步请求'''
async with session.get(url) as response:
return await response.text()
# 单线程并发,约1秒完成
results = asyncio.run(fetch_async(urls))
三,性能对比
| 指标 | 同步 | 多线程 | 异步 |
|---|---|---|---|
| 100个请求耗时 | 100秒 | 20秒(5线程) | 1秒 |
| 内存占用 | 低 | 中(每个线程~8MB) | 低 |
| CPU利用率 | 低(IO等待空转) | 中 | 高 |
| 代码复杂度 | 简单 | 中等 | 复杂 |
| 适用场景 | 脚本/批处理 | CPU密集型 | IO密集型高并发 |
四,选择建议
# 决策流程
if 任务是CPU密集型:
选择 = 多进程 # 绕过GIL限制
elif 任务是IO密集型:
if 并发连接数 < 100:
选择 = 多线程 # 简单够用
else:
选择 = 异步 # 高并发首选
elif 追求代码简单:
选择 = 同步 # 快速开发
五,混合使用策略
# 实际项目中常混合使用
import asyncio
from concurrent.futures import ProcessPoolExecutor
async def hybrid_processing():
'''
混合并发策略:
- 异步处理网络IO
- 进程池处理CPU密集型计算
'''
# 异步获取数据
async with aiohttp.ClientSession() as session:
raw_data = await fetch_data(session)
# 进程池并行处理数据(CPU密集型)
loop = asyncio.get_event_loop()
with ProcessPoolExecutor() as pool:
results = await loop.run_in_executor(
pool, cpu_intensive_process, raw_data
)
# 异步保存结果
await save_results(results)
总结
没有最好的并发模型,只有最适合的.理解原理后,根据具体场景灵活选择.
