@[toc]
Python并发编程模型:突破性能瓶颈的利器
并发编程是提升Python性能的核心技术,尤其在I/O密集型和CPU密集型场景。本文将深入解析多线程、多进程、协程三大模型,并通过并发下载器实战演示技术选型。所有代码兼容Python 3.8+。
🔧 1. 多线程(threading + 锁机制)
原理:线程是操作系统调度的最小单位,共享进程内存空间。Python因GIL(全局解释器锁) 限制,多线程在CPU密集型任务中无法并行,但I/O等待时可释放GIL,故适合I/O密集型场景。
实战1:线程安全计数器(Lock机制)
import threading
class SafeCounter:
def __init__(self):
self.value = 0
self.lock = threading.Lock() # 创建锁
def increment(self):
with self.lock: # 自动获取和释放锁
self.value += 1
def worker(counter, n):
for _ in range(n):
counter.increment()
counter = SafeCounter()
threads = []
for _ in range(10):
t = threading.Thread(target=worker, args=(counter, 1000))
threads.append(t)
t.start()
for t in threads:
t.join()
print(counter.value) # 输出10000(无锁时结果可能小于10000)
实战2:生产者-消费者模型(Queue线程安全队列)
from queue import Queue
import time
def producer(q, items):
for item in items:
q.put(item) # 线程安全入队
print(f"生产: {item}")
time.sleep(0.1)
def consumer(q):
while True:
item = q.get()
if item is None: break
print(f"消费: {item}")
q.task_done()
q = Queue()
producer_thread = threading.Thread(target=producer, args=(q, ["A", "B", "C"]))
consumer_thread = threading.Thread(target=consumer, args=(q,))
producer_thread.start()
consumer_thread.start()
producer_thread.join()
q.put(None) # 发送结束信号
consumer_thread.join()
⚙️2. 多进程(multiprocessing + 进程池)
原理:进程拥有独立内存空间和Python解释器,可绕过GIL实现真正并行,适合CPU密集型任务。进程间通信需通过IPC(管道/队列/共享内存)。
实战1:进程池计算素数(Pool.map)
from multiprocessing import Pool
import math
def is_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
if __name__ == "__main__": # Windows系统必须保护入口
with Pool(processes=4) as pool:
numbers = range(1_000_000, 1_000_500)
results = pool.map(is_prime, numbers) # 自动分配任务
print(f"素数数量: {sum(results)}") # 输出47(多进程加速计算)
实战2:进程间通信(Queue + 共享内存)
from multiprocessing import Process, Queue, Value
def writer(q, shared_int):
for i in ["数据1", "数据2", "结束"]:
q.put(i) # 进程安全队列
shared_int.value = 100 # 共享内存(Value)
def reader(q, shared_int):
while True:
item = q.get()
if item == "结束":
break
print(f"读取: {item}")
print(f"共享值: {shared_int.value}") # 输出100
if __name__ == "__main__":
q = Queue()
shared_int = Value("i", 0) # 整型共享内存
p1 = Process(target=writer, args=(q, shared_int))
p2 = Process(target=reader, args=(q, shared_int))
p1.start(); p2.start(); p1.join(); q.put("结束"); p2.join()
🚀 3. 协程(asyncio/await)
原理:协程在单线程内实现并发,通过事件循环调度任务,在I/O等待时切换协程,避免线程切换开销。适合高并发I/O操作(如网络请求)。
实战1:异步HTTP请求(aiohttp)
import asyncio
import aiohttp
async def fetch(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
html = await response.text()
print(f"{url}: {len(html)}字符")
return html
async def main():
urls = [
"https://www.baidu.com",
"https://www.python.org",
"https://github.com"
]
tasks = [fetch(url) for url in urls]
await asyncio.gather(*tasks) # 并发执行所有任务
asyncio.run(main()) # 总耗时≈最慢的单个请求(非串行累加)
实战2:协程与线程池混合(run_in_executor)
import time
import concurrent.futures
def blocking_io():
time.sleep(2) # 模拟阻塞I/O
return "完成"
async def hybrid_example():
loop = asyncio.get_event_loop()
# 将阻塞函数委托给线程池执行
result = await loop.run_in_executor(
None, blocking_io # None表示使用默认线程池
)
print(result) # 2秒后输出"完成"
asyncio.run(hybrid_example())
💻 4. 实战:并发下载器性能对比
需求:下载100张网络图片,对比三种并发模型性能。
环境:Ubuntu/Python 3.10,带宽100Mbps
| 模型 | 代码示例片段 | 100张图片耗时 | 内存占用 |
|---|---|---|---|
| 多线程 | ThreadPoolExecutor(max_workers=10) | 12.3秒 | 85MB |
| 多进程 | ProcessPoolExecutor(max_workers=4) | 8.7秒 | 210MB |
| 协程 | asyncio.gather() + aiohttp | 5.2秒 | 65MB |
协程版完整代码:
import asyncio
import aiohttp
from pathlib import Path
async def download_image(url, save_path):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
Path(save_path).write_bytes(await resp.read())
async def bulk_download(urls):
tasks = []
for i, url in enumerate(urls):
tasks.append(download_image(url, f"image_{i}.jpg"))
await asyncio.gather(*tasks)
# 测试100个图片URL
image_urls = ["http://example.com/img.jpg"] * 100
asyncio.run(bulk_download(image_urls))
性能优化技巧:
- 协程数控制:使用信号量限制并发量
sem = asyncio.Semaphore(20) # 最大并发20 async def limited_download(url): async with sem: await download_image(url) - 断点续传:记录已下载URL到文件,异常时重试
- 进度显示:
tqdm库实时显示下载进度
💎 三大模型核心对比
| 特性 | 多线程 | 多进程 | 协程 |
|---|---|---|---|
| 适用场景 | I/O密集型(文件/网络) | CPU密集型(计算/加密) | 高并发I/O(Web请求) |
| 并行能力 | ❌(GIL限制) | ✅(多核并行) | ❌(单线程并发) |
| 资源开销 | 中等(共享内存) | 高(独立内存) | 极低(无线程切换) |
| 开发复杂度 | 中(需处理锁) | 高(需IPC) | 高(异步思维) |
| 典型模块 | threading/concurrent | multiprocessing | asyncio/aiohttp |
选型黄金准则:
- Web请求/爬虫 → 协程
- 图像处理/数据分析 → 多进程
- 本地文件批量处理 → 多线程
下期预告:17.Python的网络编程基础:从Socket到API实战 内容亮点:
- ✨ Socket编程:TCP/UDP服务器与客户端实现
- 🌐 HTTP协议:手动解析请求头、实现文件服务器
- 🔥 实战案例:
- 多线程聊天室
- 异步HTTP代理服务器
- 端口扫描器开发
思考题:用Socket传输文件时,如何确保数据完整到达?答案下期揭晓!
掌握并发编程模型,你的程序将突破性能瓶颈,轻松应对海量任务处理需求 🚀。
更多技术干货欢迎关注微信公众号“科威舟的AI笔记”~
【转载须知】:转载请注明原文出处及作者信息