1. 开篇导言
在单核处理器时代,程序性能提升主要依赖时钟频率的提升。但随着摩尔定律逐渐失效(2005年后CPU主频增长停滞),单核性能提升空间收窄至每年约3%。现代应用场景如:
高并发Web服务(每秒处理数万请求)
实时数据处理(金融交易系统要求<1ms延迟)
机器学习推理(ImageNet分类从小时级到毫秒级)
这些场景推动开发者必须通过并发编程榨取硬件潜力。以典型电商系统为例,黑五期间需要同时处理:
10万+/秒的HTTP请求
实时库存更新
用户行为分析流水线
单线程架构在此场景下完全无法应对。
1.1 Python提供三层并发抽象模型:
1.1.1 多线程 (threading)
优势:轻量级、共享内存
局限:受制于GIL,适合IO密集型
适用场景:GUI事件处理、轻量级IO任务
def thread_worker():
while True:
item = queue.get()
process(item)
threads = [Thread(target=thread_worker) for _ in range(4)]
1.1.2 多进程 (multiprocessing)
优势:绕过GIL、真并行
成本:高内存开销、IPC复杂
适用场景:科学计算、数据处理
def process_worker(data_chunk):
return heavy_computation(data_chunk)
with Pool() as p:
results = p.map(process_worker, big_data)
1.1.3 协程 (asyncio)
优势:超高并发密度(可管理10K+任务)
特性:单线程事件循环、显式切换
适用场景:微服务、高频IO操作
async def handle_client(reader, writer):
data = await reader.read(1024)
response = process(data)
writer.write(response)
1.2 GIL的影响与应对
全局解释器锁(Global Interpreter Lock)是CPython的内存管理机制:
工作原理:任何字节码执行前必须获取GIL,导致:
单进程内多线程无法真正并行执行CPU操作
IO操作期间自动释放GIL
性能影响测试:
# CPU密集型多线程(无加速)
def count(n):
while n > 0:
n -= 1
threads = [Thread(target=count, args=(10**8,)) for _ in range(4)]
# 耗时 ≈ 单线程 × 4
# IO密集型多线程(有效加速)
def http_get():
requests.get('http://example.com')
threads = [Thread(target=http_get) for _ in range(100)]
# 耗时 ≈ 单线程 / 100
破解GIL的三条路径:
- 使用多进程:每个进程独立GIL
- 调用C扩展:在C层释放GIL(如NumPy)
- 采用其他解释器:Jython、IronPython
1.3 决策树
是否需要并行CPU计算?
├─ 是 → 选择多进程
└─ 否 → 是否需要高并发IO?
├─ 是 → 选择asyncio协程
└─ 否 → 使用多线程
2. 异步编程基石:asyncio
2.1 async/await语法解析
协程函数定义
async def coro_func():
result = await async_operation()
return result
async def 声明协程函数,调用时返回协程对象而非立即执行
await 表达式挂起当前协程,直到awaitable对象完成
嵌套await示例:
async def nested_await():
data = await fetch_from_db() # 第一层挂起
processed = await process(data) # 第二层挂起
return processed
协程执行原理
2.2 事件循环机制剖析
核心架构
# 手动管理事件循环
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(main())
finally:
loop.close()
事件循环阶段
检查可运行任务队列
处理定时器(sleep、延时回调)
轮询I/O事件(epoll/kqueue/select)
执行回调队列
清理关闭处理器
性能对比(默认循环 vs uvloop)
指标 | asyncio默认循环 | uvloop |
---|---|---|
每秒HTTP请求数 | 12,000 | 58,000 |
延迟(p99) | 15ms | 3ms |
内存占用 | 3MB/连接 | 1.2MB/连接 |
关键性能优化指标对比
优化策略 | 请求吞吐量提升 | 内存占用降低 | 实现复杂度 |
---|---|---|---|
启用uvloop | 400%+ | 30% | 低 |
异步DNS解析 | 15%-20% | 无 | 中 |
使用信号量控制 | 防止OOM | 50%+ | 高 |
避免阻塞调用 | 200%-300% | 无 | 中 |
3. 多进程编程体系
3. 1 多进程基础架构
Process类核心机制
from multiprocessing import Process, current_process
def worker(name):
print(f"[Child] PID: {
current_process().pid}, Name: {
name}")
if __name__ == '__main__':
p = Process(target=worker, args=('Process-1',))
p.start() # 创建新进程(内存复制)
p.join() # 等待子进程结束
print(f"[Main] Child exitcode: {
p.exitcode}")
进程关键属性对比
属性 | 父进程 | 子进程 |
---|---|---|
PID | os.getpid() | 自动生成新PID |
内存空间 | 独立拷贝 | 独立拷贝 |
文件描述符 | 继承(COW机制) | 独立操作 |
GIL | 独立实例 | 独立实例 |
3. 2. 进程间通信(IPC)机制
消息队列(Queue)实现
from multiprocessing import Process, Queue
def producer(q):
for i in range(3):
q.put(f"Message-{
i}")
print(f"Sent: Message-{
i}")
def consumer(q):
while True:
item = q.get()
if item is None: break
print(f"Received: {
item}")
if __name__ == '__main__':
q = Queue(maxsize=5)
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start(); p2.start()
p1.join()
q.put(None) # 发送终止信号
p2.join()
管道(Pipe)双向通信
from multiprocessing import Pipe
conn1, conn2 = Pipe(duplex=True) # 全双工模式
def send_task(conn):
conn.send([42, None, 'hello'])
print("Received:", conn.recv())
def recv_task(conn):
data = conn.recv(