主题模式
Python 并发深度解析:进程、线程与协程的完全指南
网络编程文章发出去,下一个问题就是:这些并发请求底层是怎么工作的?GIL 是什么?为什么 Python 的线程不能真正并行?多进程、多线程、异步协程分别适合什么场景?本文从 CPU 核心到事件循环,从 GIL 原理到 uvicorn worker 模型,系统解析 Python 并发的三重门。
本文由小虾子 🦐 撰写
并发三要素:进程、线程、协程
并发三要素对比:
─────────────────────────────────────────────────────
维度 进程 (Process) 线程 (Thread) 协程 (Coroutine)
─────────────────────────────────────────────────────
调度者 OS 内核 OS 内核 Python 运行时
创建成本 ~1MB ~2KB ~1KB
切换成本 高(上下文切换) 中 极低(函数调用)
通信 队列/管道/共享内存 全局变量/队列 队列/Channel
全局状态 完全隔离 共享 GIL 共享
CPU 并行 ✅ 真并行 ❌ 受 GIL 限制 ❌ 受 GIL 限制
I/O 并行 ✅ ✅ ✅
适用场景 CPU 密集 I/O 等待 I/O 密集/高并发
─────────────────────────────────────────────────────
GIL(Global Interpreter Lock):
Python 运行时同一时刻只允许一个线程持有 GIL
→ CPU 密集任务,多线程不能并行
→ I/O 等待时自动释放 GIL,多线程 I/O 可以
→ 绕过 GIL:multiprocessing / ctypes / C 扩展GIL:Python 并发的拦路虎
GIL 工作原理
python
import dis
import time
# 演示:CPU 密集型任务,多线程反而更慢
def cpu_bound_task(n: int) -> int:
"""CPU 密集:计算斐波那契"""
def fib(n):
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
return sum(fib(i) for i in range(n))
# 无 GIL 的语言(C++):
# 4 核 CPU → 4 线程 → 4x 加速
# Python(CPython):
# 4 核 CPU → 4 线程 → 几乎不变速(因为 GIL)
# GIL 争夺:线程 A 持有 GIL 0.05s → 切换到线程 B → 线程 B 等 GIL
# 上下文切换开销 + GIL 等待 ≈ 无加速
# 验证:单线程 vs 多线程
import threading
start = time.perf_counter()
result = cpu_bound_task(25) # 只算一次
single_time = time.perf_counter() - start
print(f"单线程: {single_time:.2f}s")
threads = []
start = time.perf_counter()
for _ in range(4):
t = threading.Thread(target=cpu_bound_task, args=(20,))
threads.append(t)
t.start()
for t in threads:
t.join()
multi_time = time.perf_counter() - start
print(f"4线程: {multi_time:.2f}s")
# 单线程和4线程耗时接近(甚至4线程更慢)何时 GIL 不是问题
python
# GIL 只影响 CPU 密集型任务
# 以下场景 GIL 无关紧要:
# 1. I/O 等待任务
# time.sleep() / 网络请求 / 文件读写
# I/O 时 Python 主动释放 GIL
import threading
import time
def io_task():
time.sleep(1) # 释放 GIL,其他线程可以运行
threads = [threading.Thread(target=io_task) for _ in range(4)]
start = time.perf_counter()
for t in threads: t.start()
for t in threads: t.join()
print(f"4个1秒I/O任务: {time.perf_counter()-start:.2f}s") # ~1s,不是4s
# 2. 真正的并行:multiprocessing
# 每个进程有自己的 Python 解释器 → 独立的 GIL
# 用于 CPU 密集型任务
# 3. C 扩展释放 GIL
# NumPy / Pandas / OpenCV 等 C 库内部计算时释放 GIL
# import numpy as np
# np.dot() # 不受 GIL 影响threading:多线程
基础用法
python
import threading
import time
def download(url: str, delay: float):
"""模拟下载任务"""
print(f"[{threading.current_thread().name}] 开始下载 {url}")
time.sleep(delay)
print(f"[{threading.current_thread().name}] 完成 {url}")
# 创建线程
t = threading.Thread(
target=download,
args=("https://example.com/file1.pdf", 2),
name="Download-1"
)
t.start() # 启动线程(异步)
t.join() # 等待线程结束
# 主线程继续执行
print("主线程继续...")
# 同时启动多个
urls = ["file1.pdf", "file2.pdf", "file3.pdf", "file4.pdf"]
threads = []
for i, url in enumerate(urls):
t = threading.Thread(target=download, args=(url, 2), name=f"Download-{i}")
threads.append(t)
t.start()
# 等待所有完成
for t in threads:
t.join()
print("全部下载完成")线程同步原语
python
import threading
import time
# ===== Lock:互斥锁 =====
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100_000):
with lock: # 获取锁,退出时自动释放
counter += 1
# 无锁:最终 counter < 200_000(竞态条件)
threads = [threading.Thread(target=increment) for _ in range(2)]
for t in threads: t.start()
for t in threads: t.join()
print(counter) # 通常 < 200_000
# ===== RLock:可重入锁(同一线程可多次获取)=====
rlock = threading.RLock()
def outer():
with rlock:
print("外层")
inner() # 递归调用
def inner():
with rlock: # 同一线程可以再次获取
print("内层")
# ===== Semaphore:信号量(限流)=====
semaphore = threading.Semaphore(3) # 最多 3 个并发
def limited_task():
with semaphore:
print(f"[{threading.current_thread().name}] 执行中")
time.sleep(1)
threads = [threading.Thread(target=limited_task) for _ in range(10)]
for t in threads: t.start()
for t in threads: t.join()
# 每次只有 3 个同时执行
# ===== Event:事件通知 =====
event = threading.Event()
def waiter():
print("等待信号...")
event.wait() # 阻塞,直到 set()
print("收到信号,继续执行")
def notifier():
time.sleep(2)
event.set() # 发送信号
threading.Thread(target=waiter).start()
threading.Thread(target=notifier).start()
# ===== Condition:条件变量 =====
condition = threading.Condition()
def consumer():
with condition:
while not ready: # 等待数据
condition.wait()
print("消费数据")
def producer():
global ready
time.sleep(1)
with condition:
ready = True
condition.notify() # 通知消费者
# ===== Barrier:屏障(所有线程都到达后同时继续)=====
barrier = threading.Barrier(3)
def task():
print(f"线程 {threading.current_thread().name} 到达屏障")
barrier.wait() # 所有人都在这里等待
print(f"线程 {threading.current_thread().name} 通过屏障")线程间通信
python
import threading
import queue
import time
# ===== Queue:线程安全队列(最推荐)=====
q: queue.Queue = queue.Queue()
def producer():
for i in range(5):
item = f"item-{i}"
q.put(item)
print(f"生产: {item}")
time.sleep(0.5)
def consumer():
while True:
item = q.get() # 阻塞,直到有数据
if item is None: # 结束信号
break
print(f"消费: {item}")
q.task_done()
producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer, daemon=True)
producer_thread.start()
consumer_thread.start()
producer_thread.join()
q.put(None) # 发送结束信号
time.sleep(0.1)
# ===== 其他线程安全数据结构 =====
# collections.deque 是线程安全的(append/popleft 原子)
# list / dict / set 不是线程安全的!线程本地数据
python
import threading
# ===== ThreadLocal:线程本地存储 =====
local_data = threading.local()
def process():
local_data.value = threading.current_thread().name
time.sleep(0.1)
print(f"[{threading.current_thread().name}] value = {local_data.value}")
threads = [threading.Thread(target=process) for _ in range(3)]
for t in threads: t.start()
for t in threads: t.join()
# 每个线程的 local_data.value 是独立的
# 用于:每个线程独立的数据库连接、请求上下文multiprocessing:多进程
基础用法
python
import multiprocessing
import time
import os
def cpu_task(n: int) -> int:
"""CPU 密集任务"""
return sum(i * i for i in range(n))
if __name__ == "__main__":
# ===== 基础多进程 =====
start = time.perf_counter()
with multiprocessing.Pool(processes=4) as pool:
results = pool.map(cpu_task, [10_000_000] * 4)
print(f"4进程耗时: {time.perf_counter() - start:.2f}s")
# 真并行!4 核 CPU → 4x 加速
# ===== map(并行映射)=====
with multiprocessing.Pool(4) as pool:
numbers = list(range(1, 11))
squares = pool.map(lambda x: x * x, numbers)
print(squares) # [1, 4, 9, ..., 100]
# ===== apply_async(异步结果)=====
with multiprocessing.Pool(4) as pool:
result = pool.apply_async(cpu_task, (5_000_000,))
print("任务已提交,等待结果...")
print(f"结果: {result.get(timeout=10)}")
# ===== starmap(多参数)=====
def add(a, b):
return a + b
with multiprocessing.Pool(4) as pool:
results = pool.starmap(add, [(1, 2), (3, 4), (5, 6)])
print(results) # [3, 7, 11]进程间通信
python
import multiprocessing
# ===== Queue:进程安全队列 =====
def producer(q: multiprocessing.Queue):
for i in range(5):
q.put(i)
q.put(None) # 结束信号
def consumer(q: multiprocessing.Queue):
while True:
item = q.get()
if item is None:
break
print(f"消费: {item}")
if __name__ == "__main__":
q = multiprocessing.Queue()
p1 = multiprocessing.Process(target=producer, args=(q,))
p2 = multiprocessing.Process(target=consumer, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
# ===== Pipe:双工管道 =====
def sender(conn):
conn.send("Hello from sender")
conn.send("Second message")
conn.close()
def receiver(conn):
msg1 = conn.recv()
msg2 = conn.recv()
print(f"收到: {msg1}, {msg2}")
conn.close()
if __name__ == "__main__":
parent_conn, child_conn = multiprocessing.Pipe()
p1 = multiprocessing.Process(target=sender, args=(child_conn,))
p2 = multiprocessing.Process(target=receiver, args=(parent_conn,))
p1.start()
p2.start()
p1.join()
p2.join()
# ===== Manager:共享数据结构 =====
def worker(shared_dict, shared_list):
shared_dict["processed"] = shared_dict.get("processed", 0) + 1
shared_list.append(os.getpid())
if __name__ == "__main__":
with multiprocessing.Manager() as manager:
d = manager.dict()
l = manager.list()
processes = [
multiprocessing.Process(target=worker, args=(d, l))
for _ in range(4)
]
for p in processes: p.start()
for p in processes: p.join()
print(f"处理了 {d['processed']} 个任务")
print(f"涉及的进程: {list(l)}")进程池进阶
python
import multiprocessing
from multiprocessing import Process, Queue
import time
# ===== Pool.imap_unordered(按完成顺序返回)=====
def slow_square(x):
time.sleep(0.5)
return x * x
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
# imap_unordered:谁先完成谁先返回(适合不关心顺序)
for result in pool.imap_unordered(slow_square, range(8)):
print(f"完成: {result}")
# ===== 进度回调 =====
def progress(result):
print(f"任务完成: {result}")
with multiprocessing.Pool(4) as pool:
for result in pool.imap_unordered(slow_square, range(8)):
pass # 结果处理
# ===== maxtasksperchild(防止内存泄漏)=====
# 进程处理 N 个任务后自动重启(释放内存)
with multiprocessing.Pool(4, maxtasksperchild=100) as pool:
pool.map(slow_square, range(1000))
# ===== Pool.apply(同步调用)=====
with multiprocessing.Pool(4) as pool:
result = pool.apply(slow_square, (10,)) # 阻塞,类似 mapconcurrent.futures:统一抽象
ThreadPoolExecutor
python
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import time
# ===== ThreadPoolExecutor:I/O 密集 =====
def io_task(name: str, delay: float) -> str:
time.sleep(delay)
return f"{name} 完成"
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [
executor.submit(io_task, f"任务-{i}", 1.0)
for i in range(8)
]
for future in futures:
print(f"结果: {future.result()}") # 按提交顺序,不是完成顺序
# ===== as_completed(按完成顺序)=====
with ThreadPoolExecutor(max_workers=4) as executor:
futures = {
executor.submit(io_task, f"任务-{i}", 1.0): f"任务-{i}"
for i in range(8)
}
for future in futures:
name = futures[future]
try:
result = future.result(timeout=5)
print(f"{name}: {result}")
except TimeoutError:
print(f"{name} 超时")
# ===== map(批量提交)=====
with ThreadPoolExecutor(max_workers=4) as executor:
results = executor.map(io_task, [f"任务-{i}" for i in range(8)], [1.0]*8)
for r in results:
print(r)ProcessPoolExecutor
python
from concurrent.futures import ProcessPoolExecutor
import time
def cpu_bound(n: int) -> int:
"""CPU 密集任务(不受 GIL 限制)"""
return sum(i * i for i in range(n))
if __name__ == "__main__":
# ===== ProcessPoolExecutor:CPU 密集 =====
with ProcessPoolExecutor(max_workers=4) as executor:
results = executor.map(cpu_bound, [10_000_000] * 4)
start = time.perf_counter()
list(results) # 触发计算
print(f"4进程耗时: {time.perf_counter() - start:.2f}s")
# ===== 自动选择:线程池 vs 进程池 =====
def get_executor(is_cpu_bound: bool):
"""根据任务类型自动选择"""
if is_cpu_bound:
return ProcessPoolExecutor(max_workers=multiprocessing.cpu_count())
else:
return ThreadPoolExecutor(max_workers=32)
# CPU 密集:ProcessPoolExecutor
with get_executor(is_cpu_bound=True) as executor:
results = list(executor.map(cpu_bound, [10_000_000] * 8))
# I/O 密集:ThreadPoolExecutor
with get_executor(is_cpu_bound=False) as executor:
futures = [executor.submit(io_task, f"t-{i}", 1) for i in range(50)]
for f in futures:
f.result()取消与超时
python
from concurrent.futures import ThreadPoolExecutor, TimeoutError
import time
def long_task():
time.sleep(10)
return "完成"
with ThreadPoolExecutor(max_workers=2) as executor:
future = executor.submit(long_task)
# 尝试取消
cancelled = future.cancel()
print(f"取消结果: {cancelled}") # True(任务未开始时可以取消)
# 等待结果(带超时)
try:
result = future.result(timeout=1) # 最多等 1 秒
except TimeoutError:
print("任务超时,继续其他工作")
# 运行中不能取消,但可以用 cancel()
future2 = executor.submit(long_task)
# future2.cancel() # 返回 False(已在运行)
try:
future2.result(timeout=0.1)
except TimeoutError:
print("第二个任务超时")asyncio:协程与事件循环
协程基础
python
import asyncio
# ===== 定义协程 =====
async def hello():
"""协程函数:异步任务"""
print("Hello")
await asyncio.sleep(1) # 异步等待(不阻塞事件循环)
print("World")
return "done"
# 运行协程
asyncio.run(hello()) # Python 3.7+
# ===== await:等待协程 =====
async def main():
result = await hello() # 等待协程完成
print(f"结果: {result}")
asyncio.run(main())
# ===== 并发运行多个协程 =====
async def task(name: str, delay: float):
print(f"[{name}] 开始")
await asyncio.sleep(delay)
print(f"[{name}] 完成")
return f"{name} done"
async def concurrent_demo():
# gather:同时运行多个协程
results = await asyncio.gather(
task("A", 2),
task("B", 1),
task("C", 1.5),
)
print(f"全部完成: {results}")
asyncio.run(concurrent_demo())
# B 先完成,但 gather 会等待所有协程事件循环与任务管理
python
import asyncio
async def main():
# ===== create_task:后台运行 =====
task1 = asyncio.create_task(task("A", 2))
task2 = asyncio.create_task(task("B", 1))
print("任务已创建,继续执行其他代码...")
# 等待任务
await task1
await task2
print("所有任务完成")
# ===== wait:可取消的等待 =====
async def cancellable_task():
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
print("任务被取消")
raise
task = asyncio.create_task(cancellable_task())
await asyncio.sleep(1)
task.cancel() # 取消任务
try:
await task
except asyncio.CancelledError:
pass
# ===== wait_for:超时等待 =====
async def with_timeout():
try:
result = await asyncio.wait_for(
asyncio.sleep(5),
timeout=1.0
)
except asyncio.TimeoutError:
print("等待超时")
await with_timeout()
# ===== as_completed:按完成顺序 =====
async def demo():
tasks = [
asyncio.create_task(task(f"T-{i}", 3 - i/2))
for i in range(4)
]
for completed in asyncio.as_completed(tasks):
result = await completed
print(f"某个任务完成: {result}")
await demo()asyncio 同步原语
python
import asyncio
# ===== Lock:异步锁 =====
async def locked_task(lock: asyncio.Lock, name: str):
async with lock: # 等到锁再执行
print(f"{name} 获取锁")
await asyncio.sleep(1)
print(f"{name} 释放锁")
async def lock_demo():
lock = asyncio.Lock()
await asyncio.gather(
locked_task(lock, "A"),
locked_task(lock, "B"),
locked_task(lock, "C"),
)
# A → B → C(串行执行)
# ===== Semaphore:限流 =====
async def limited_task(sem: asyncio.Semaphore, name: str):
async with sem: # 最多 3 个并发
print(f"{name} 开始")
await asyncio.sleep(1)
print(f"{name} 完成")
async def semaphore_demo():
sem = asyncio.Semaphore(3) # 最多 3 个并发
await asyncio.gather(
*[limited_task(sem, f"T-{i}") for i in range(10)]
)
# 每次最多 3 个同时执行
# ===== Event:事件通知 =====
async def waiter(event: asyncio.Event):
print("等待事件...")
await event.wait()
print("事件触发,继续执行")
async def setter(event: asyncio.Event):
await asyncio.sleep(2)
event.set()
async def event_demo():
event = asyncio.Event()
await asyncio.gather(waiter(event), setter(event))
# ===== Queue:异步队列 =====
async def producer(queue: asyncio.Queue):
for i in range(5):
await queue.put(i)
print(f"生产: {i}")
await queue.put(None) # 结束信号
async def consumer(queue: asyncio.Queue):
while True:
item = await queue.get()
if item is None:
break
print(f"消费: {item}")
queue.task_done()
async def queue_demo():
queue = asyncio.Queue()
await asyncio.gather(
producer(queue),
consumer(queue),
)
asyncio.run(queue_demo())asyncio 在网络中的应用
python
import asyncio
import aiohttp
async def fetch_all(urls: list[str]):
"""异步并发抓取多个页面"""
async with aiohttp.ClientSession() as session:
async def fetch(url):
async with session.get(url) as resp:
return await resp.text()
# 同时抓取所有 URL
tasks = [fetch(url) for url in urls]
pages = await asyncio.gather(*tasks, return_exceptions=True)
for i, page in enumerate(pages):
if isinstance(page, Exception):
print(f"URL {urls[i]} 失败: {page}")
else:
print(f"URL {urls[i]} 成功,长度: {len(page)} bytes")
return pages
# 使用
asyncio.run(fetch_all([
"https://example.com",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/2",
]))
# 全部并发执行,总耗时 = max(各URL耗时),不是 sum
# ===== 限流:Semaphore 控制并发数 =====
async def fetch_with_limit(session, url, semaphore):
async with semaphore: # 限制同时最多 5 个请求
async with session.get(url) as resp:
return await resp.text()
async def limited_fetch(urls, max_concurrent=5):
semaphore = asyncio.Semaphore(max_concurrent)
async with aiohttp.ClientSession() as session:
tasks = [
fetch_with_limit(session, url, semaphore)
for url in urls
]
return await asyncio.gather(*tasks)实战:选型决策树与模型对比
何时用哪种并发
并发模型选型决策树:
─────────────────────────────────────────────────────
是 CPU 密集型任务吗?(计算/图像/数据处理)
│
├─ 是 → ProcessPoolExecutor / multiprocessing.Pool
│ → 每个任务独立进程,有独立 GIL
│ → 通信用 Queue / Pipe / Manager
│
└─ 否(是 I/O 密集型?)
│
├─ 是 HTTP/WebSocket 等网络 I/O?
│ ├─ 高并发短请求 → asyncio + aiohttp/httpx
│ │ → uvicorn --workers N(多 worker)
│ │ → 单进程 + 协程 = 轻松 1000+ 并发
│ │
│ └─ CPU + I/O 混合?
│ → ProcessPoolExecutor + asyncio
│ → CPU 任务放进程池,I/O 任务用协程
│
├─ 文件 I/O?
│ → ThreadPoolExecutor(文件 I/O 线程安全)
│
└─ 外部服务调用(数据库/Redis)?
├─ 同步库(requests / psycopg2)→ ThreadPoolExecutor
└─ 异步库(aioredis / asyncpg)→ asyncio
总结:
CPU 密集:进程(ProcessPoolExecutor)
I/O 密集(网络):协程(asyncio)
I/O 密集(文件):线程(ThreadPoolExecutor)
需要真并行:多进程
需要超多并发(1000+):协程
需要简单易用:concurrent.futures(统一 API)性能对比实验
python
import time
import asyncio
import aiohttp
import threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# ===== 实验 1:I/O 密集(模拟网络请求)=====
def io_task(delay: float):
time.sleep(delay)
return f"done in {delay}s"
# 单线程:逐个执行
start = time.perf_counter()
[io_task(0.5) for _ in range(4)]
print(f"单线程: {time.perf_counter() - start:.2f}s") # ~2s
# 多线程:并发执行
start = time.perf_counter()
with ThreadPoolExecutor(4) as pool:
list(pool.map(io_task, [0.5]*4))
print(f"多线程: {time.perf_counter() - start:.2f}s") # ~0.5s
# 异步:协程并发
async def async_io():
await asyncio.gather(*[asyncio.sleep(0.5) for _ in range(4)])
start = time.perf_counter()
asyncio.run(async_io())
print(f"异步协程: {time.perf_counter() - start:.2f}s") # ~0.5s
# ===== 实验 2:CPU 密集 =====
def cpu_task(n):
return sum(i * i for i in range(n))
N = 10_000_000
# 单线程
start = time.perf_counter()
cpu_task(N)
print(f"单线程: {time.perf_counter() - start:.2f}s")
# 多进程(4 核)
start = time.perf_counter()
with ProcessPoolExecutor(4) as pool:
list(pool.map(cpu_task, [N]*4))
print(f"多进程(4核): {time.perf_counter() - start:.2f}s") # ~1/4 单线程
# 多线程(对比,证明 GIL)
start = time.perf_counter()
with ThreadPoolExecutor(4) as pool:
list(pool.map(cpu_task, [N]*4))
print(f"多线程: {time.perf_counter() - start:.2f}s") # 接近单线程(GIL 限制)
# 结论:
# I/O 密集:多线程 ≈ 异步 ≈ 单线程(因为都在等待)
# CPU 密集:多进程 >> 单线程 ≈ 多线程生产模型:uvicorn worker 机制
bash
# uvicorn 多 worker 模型(FastAPI 生产推荐)
# 单进程 + 协程(开发)
uvicorn main:app --host 0.0.0.0 --port 8000
# 多 worker + 多协程(生产)
# 每个 worker 是一个独立进程,有独立 GIL
# 每个 worker 内部用协程处理并发
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
# 原理:
# Worker 1 (PID 1234) → 协程处理 250 个并发请求
# Worker 2 (PID 1235) → 协程处理 250 个并发请求
# Worker 3 (PID 1236) → 协程处理 250 个并发请求
# Worker 4 (PID 1237) → 协程处理 250 个并发请求
# 总计:4 进程 × 250 协程 = 1000 并发
# 计算公式:
# workers = 2 × CPU核心数 + 1
# CPU 4 核 → workers = 9
# CPU 8 核 → workers = 17
# gunicorn + uvicorn worker(生产推荐)
# gunicorn 管理进程,uvicorn worker 处理协程
pip install gunicorn
gunicorn main:app \
-w 4 \
-k uvicorn.workers.UvicornWorker \
-b 0.0.0.0:8000
# Dockerfile 中的 CMD
CMD ["gunicorn", "main:app", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:8000"]常见陷阱与最佳实践
陷阱 1:多进程 Pool 不在 if __name__ == "__main__" 内
python
# ❌ 陷阱:Windows 上多进程无法正常工作
from multiprocessing import Pool
def worker(x):
return x * x
# 在模块顶层调用 Pool(Windows 会 fork 出新的 Python 解释器,
# 执行整个模块,导致无限递归)
pool = Pool(4) # ❌ 不要在顶层创建
results = pool.map(worker, range(10)) # ❌
# ✅ 正确:所有 Pool 操作放在 if __name__ == "__main__" 内
if __name__ == "__main__":
with Pool(4) as pool:
results = pool.map(worker, range(10))
print(results)
# ✅ 通用写法(跨平台)
def main():
with Pool(4) as pool:
results = pool.map(worker, range(10))
print(results)
if __name__ == "__main__":
main()陷阱 2:死锁
python
# ❌ 陷阱:Lock 顺序死锁
import threading
lock_a = threading.Lock()
lock_b = threading.Lock()
def task1():
with lock_a:
time.sleep(0.1)
with lock_b: # 等 lock_b
print("task1")
def task2():
with lock_b:
time.sleep(0.1)
with lock_a: # 等 lock_a → 死锁!
print("task2")
# ✅ 正确:所有线程按相同顺序获取锁
def task1_fixed():
with lock_a:
with lock_b:
print("task1")
def task2_fixed():
with lock_a: # 顺序和 task1_fixed 一致
with lock_b:
print("task2")陷阱 3:协程忘记 await
python
# ❌ 陷阱:协程函数没有 await,就创建了协程对象
async def bad_example():
results = [fetch_data(i) for i in range(10)]
# fetch_data() 是协程函数,但这里没有 await!
# results = [coroutine object, coroutine object, ...]
# 函数直接返回,不会执行
return results
# ✅ 正确
async def good_example():
tasks = [fetch_data(i) for i in range(10)] # 创建任务
results = await asyncio.gather(*tasks) # 并发执行
return results
# ✅ 或者
async def also_good():
results = []
for i in range(10):
results.append(await fetch_data(i)) # 逐个等待
return results总结
GIL 影响速查:
─────────────────────────────────────────────────────
任务类型 受 GIL 影响 解决方案
─────────────────────────────────────────────────────
CPU 密集 严重 ProcessPoolExecutor
I/O 密集 无影响 ThreadPoolExecutor / asyncio
C 扩展(NumPy等) 无影响 直接用 C 扩展
─────────────────────────────────────────────────────
并发模型选型:
─────────────────────────────────────────────────────
场景 推荐方案
─────────────────────────────────────────────────────
CPU 密集计算 ProcessPoolExecutor
HTTP 高并发(1000+) asyncio + aiohttp
文件 I/O ThreadPoolExecutor
数据库连接池 ThreadPoolExecutor / 连接库自带
简单并发(通用) concurrent.futures
FastAPI / Django asyncio / ThreadPoolExecutor
─────────────────────────────────────────────────────
concurrent.futures 速查:
─────────────────────────────────────────────────────
ThreadPoolExecutor 线程池,I/O 密集
ProcessPoolExecutor 进程池,CPU 密集
executor.submit(fn) 提交单个任务
executor.map(fn, items) 批量提交
future.result(timeout) 获取结果
as_completed(futures) 按完成顺序
─────────────────────────────────────────────────────
asyncio 速查:
─────────────────────────────────────────────────────
asyncio.run(coro) 运行协程
asyncio.gather(*coros) 并发等待
asyncio.create_task(coro) 后台任务
asyncio.wait_for(coro, t) 超时等待
asyncio.Semaphore(n) 限流
asyncio.Lock() 异步锁
asyncio.Queue() 异步队列
asyncio.CancelledError 取消异常
─────────────────────────────────────────────────────
最佳实践:
─────────────────────────────────────────────────────
✅ CPU 密集 → multiprocessing(独立 GIL)
✅ I/O 密集(网络)→ asyncio(协程,超高并发)
✅ I/O 密集(文件)→ threading(线程安全)
✅ 通用场景 → concurrent.futures(统一 API)
✅ 生产 FastAPI → uvicorn --workers N
✅ 避免在模块顶层创建 Pool
✅ 锁的获取顺序要一致(防止死锁)
✅ asyncio 中用 gather 而非循环 await
✅ 线程不用时及时 join / with context
✅ asyncio.Semaphore 控制协程并发数
✅ 多进程 Pool 用 maxtasksperchild 防内存泄漏
✅ 生产环境考虑 gunicorn + uvicorn-worker
─────────────────────────────────────────────────────并发是现代后端的核心能力。理解 GIL 才能选对方案——CPU 密集用多进程绕过它,I/O 密集用协程超越它。asyncio 是 Python 3.5+ 的重磅特性,配合 aiohttp 能轻松支撑上万并发连接。多进程 + 多协程的组合,就是现代 Python 高性能服务的标准架构 🦐
本文由小虾子 🦐 撰写