引言
Python 提供了三种主要的并发模型,每种都有其适用场景:
- multiprocessing:通过创建独立进程绕过 GIL,实现真正的并行计算,最适合 CPU 密集型任务。
- threading:使用轻量级线程,共享内存空间,适合 I/O 密集型任务,但受 GIL 限制。
- asyncio:使用协程和事件循环实现单线程并发,极低的资源消耗,最适合高并发 I/O 密集型任务。
主要章节概览
一、并发编程基础概念
- 进程与线程的本质区别
- 并发与并行的概念
- Python 三种并发模型对比
二、全局解释器锁(GIL)
- GIL 的设计原因和工作机制
- GIL 对性能的影响
- Python 3.13 的 No-GIL 选项
三、多进程编程(multiprocessing)
- Process 类的使用
- 进程池(Pool)的高级应用
- 进程间通信(Queue、Pipe、共享内存)
四、多线程编程(threading)
- Thread 类的使用
- 线程同步机制(Lock、RLock、Semaphore、Event、Condition)
- 线程池(ThreadPoolExecutor)
五、异步编程(asyncio)
- 协程和事件循环
- async/await 语法
- 异步上下文管理器和迭代器
六、并发模型选择指南
- 任务类型分析(决策树)
- 性能对比实验代码
七、常见陷阱与调试技巧
- 竞态条件和死锁
- 调试方法
八、性能优化技巧
一、并发编程基础概念
1.1 进程与线程的本质
在深入 Python 的并发编程之前,我们需要理解两个核心概念:进程(Process)和线程(Thread)。
进程是操作系统进行资源分配的最小单元。每个进程都有独立的内存空间、系统资源和执行环境。当你启动一个 Python 程序时,操作系统就会创建一个进程,为其分配内存、文件句柄等资源。进程之间相互独立,一个进程的崩溃不会影响其他进程。
线程是 CPU 调度的最小单元。一个进程至少包含一个线程(主线程),也可以创建多个线程。同一进程内的所有线程共享该进程的内存空间和资源,这使得线程间的通信相对简单,但也带来了数据竞争和同步的挑战。
1.2 并发与并行的区别
并发(Concurrency)是指多个任务在时间上重叠执行的能力。任务可能不是真正同时运行,而是通过快速切换给人一种同时执行的感觉。这种模式特别适合 I/O 密集型任务,因为任务大部分时间都在等待外部资源。
并行(Parallelism)则是指多个任务在同一时刻真正同时执行,这需要多核处理器的支持。每个任务运行在不同的 CPU 核心上,实现真正的同时计算。这种模式最适合 CPU 密集型任务。
1.3 Python 并发模型概览
Python 提供了三种主要的并发模型:
| 模型 | 执行方式 | GIL影响 | 适用场景 | 资源消耗 |
|---|---|---|---|---|
| threading | 多线程,单进程 | 受限于GIL | I/O密集型任务 | 低 |
| multiprocessing | 多进程 | 完全独立 | CPU密集型任务 | 高 |
| asyncio | 协程,单线程 | 无影响 | 高并发I/O任务 | 极低 |
二、全局解释器锁(GIL)
2.1 GIL 的本质与设计原因
全局解释器锁(Global Interpreter Lock,简称 GIL)是 CPython 解释器的一个关键特性,也是 Python 并发编程中最具争议的话题之一。
GIL 是什么?
GIL 是一个互斥锁(mutex),它确保在任何时刻只有一个线程能够执行 Python 字节码。即使在多核处理器上运行多线程 Python 程序,同一时刻也只有一个线程在执行。
为什么需要 GIL?
GIL 的引入主要基于以下两个原因:
- 简化内存管理:Python 使用引用计数(reference counting)进行内存管理。每个对象都有一个引用计数器,记录有多少引用指向该对象。当引用计数降至零时,对象会被自动回收。这个计数器需要在多线程环境下保持线程安全。
- 避免死锁:如果为每个对象或数据结构单独加锁,就会存在多个锁,容易导致死锁。GIL 通过使用单一的全局锁简化了这个问题,避免了复杂的锁管理机制。
2.2 GIL 的工作机制
当 Python 线程执行时,它必须先获取 GIL。一旦获得 GIL,线程就可以执行 Python 字节码,直到以下情况之一发生:
- 线程主动释放 GIL(例如执行 I/O 操作)
- 线程执行了一定数量的字节码指令(默认约为 5ms 的执行时间)
- 线程被操作系统调度器强制挂起
import dis
def increment(n):
n += 1
return n
# 查看字节码指令
dis.dis(increment)
输出显示 n += 1 操作实际上由多个字节码指令组成:
LOAD_FAST (加载变量n)
LOAD_CONST (加载常量1)
INPLACE_ADD (执行加法)
STORE_FAST (存储结果)
这意味着看似原子的操作在 Python 层面并不是原子的,因此在多线程环境下仍然需要同步机制。
2.3 GIL 的性能影响
对 CPU 密集型任务的影响
在 CPU 密集型任务中,GIL 会严重限制多线程的性能。由于同一时刻只有一个线程能执行,多线程程序的性能可能还不如单线程程序,因为线程切换会带来额外的开销。
对 I/O 密集型任务的影响
在 I/O 密集型任务中,GIL 的影响相对较小。当线程执行 I/O 操作时(如网络请求、文件读写),它会主动释放 GIL,允许其他线程执行。因此,多线程在处理 I/O 密集型任务时仍然有效。
2.4 Python 3.13 的 No-GIL 选项
2024年10月,Python 3.13 引入了实验性的**自由线程(free-threaded)**构建选项,允许在编译时禁用 GIL。这个重大变化基于 PEP 703 提案,采用了以下技术:
- 偏向引用计数(Biased Reference Counting):优化引用计数的更新机制
- 延迟引用计数(Deferred Reference Counting):延迟某些引用计数的更新
- 不朽对象(Immortal Objects):某些对象永不销毁,避免引用计数操作
然而,移除 GIL 是一个渐进的过程,预计需要数年时间才能成为默认选项。
三、多进程编程(multiprocessing)
3.1 multiprocessing 模块概述
multiprocessing 模块是 Python 标准库中用于并行处理的核心模块。它通过创建独立的进程来绕过 GIL 的限制,实现真正的并行计算。
核心特性:
- 每个进程拥有独立的 Python 解释器和内存空间
- 完全避开 GIL 的限制
- 适合 CPU 密集型任务
- 提供类似 threading 模块的 API
3.2 进程创建方法
Python 提供了三种进程启动方法:
- spawn(Windows 默认):父进程启动全新的 Python 解释器进程,子进程只继承运行所需的必要资源
- fork(Unix 默认):父进程使用 os.fork() 创建子进程,子进程复制父进程的内存空间
- forkserver:启动服务器进程,后续进程创建由服务器进程 fork 生成
import multiprocessing
# 设置启动方法(需要在主程序开始处调用)
multiprocessing.set_start_method('spawn')
3.3 Process 类的使用
基本用法
from multiprocessing import Process
import os
def worker(name):
print(f'Worker {name} (PID: {os.getpid()}) starting')
# 执行一些计算密集型任务
result = sum(i * i for i in range(10**7))
print(f'Worker {name} finished with result: {result}')
if __name__ == '__main__':
# 创建进程
processes = []
for i in range(4):
p = Process(target=worker, args=(f'Process-{i}',))
processes.append(p)
p.start() # 启动进程
# 等待所有进程完成
for p in processes:
p.join()
print('All processes completed')
继承 Process 类
from multiprocessing import Process
import time
class CustomWorker(Process):
def __init__(self, task_id):
super().__init__()
self.task_id = task_id
def run(self):
"""重写 run 方法定义进程行为"""
print(f'Task {self.task_id} starting')
time.sleep(2)
print(f'Task {self.task_id} completed')
if __name__ == '__main__':
workers = [CustomWorker(i) for i in range(3)]
for w in workers:
w.start()
for w in workers:
w.join()
3.4 进程池(Pool)
进程池提供了更高级的并行处理接口,特别适合批量处理大量任务。
Pool 的核心方法
from multiprocessing import Pool
import time
def compute_square(n):
"""计算平方"""
time.sleep(0.1) # 模拟计算
return n * n
if __name__ == '__main__':
# 创建进程池(默认使用 CPU 核心数)
with Pool(processes=4) as pool:
# map: 阻塞式批量处理
numbers = range(20)
results = pool.map(compute_square, numbers)
print(f'map results: {results}')
# map_async: 非阻塞式批量处理
async_result = pool.map_async(compute_square, numbers)
# 可以继续做其他事情
print('Doing other work...')
# 获取结果(会阻塞直到完成)
results = async_result.get()
print(f'map_async results: {results}')
# apply: 处理单个任务(阻塞)
result = pool.apply(compute_square, (10,))
print(f'apply result: {result}')
# apply_async: 处理单个任务(非阻塞)
async_result = pool.apply_async(compute_square, (10,))
result = async_result.get()
print(f'apply_async result: {result}')
Pool vs Process 对比
| 特性 | Pool | Process |
|---|---|---|
| 任务类型 | 同质化批量任务 | 异质化独立任务 |
| 进程管理 | 自动管理进程池 | 手动管理每个进程 |
| 资源复用 | 进程可复用 | 每次创建新进程 |
| 适用场景 | 大量相似任务 | 少量不同任务 |
| 易用性 | 简单易用 | 需要更多控制 |
3.5 进程间通信(IPC)
由于进程间内存隔离,需要专门的机制来实现数据交换。
Queue(队列)
from multiprocessing import Process, Queue
def producer(queue):
"""生产者进程"""
for i in range(5):
print(f'Producing item {i}')
queue.put(i)
queue.put(None) # 发送结束信号
def consumer(queue):
"""消费者进程"""
while True:
item = queue.get()
if item is None:
break
print(f'Consuming item {item}')
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer, args=(q,))
p2 = Process(target=consumer, args=(q,))
p1.start()
p2.start()
p1.join()
p2.join()
Pipe(管道)
from multiprocessing import Process, Pipe
def sender(conn):
"""发送端"""
messages = ['Hello', 'World', 'from', 'Pipe']
for msg in messages:
conn.send(msg)
conn.close()
def receiver(conn):
"""接收端"""
while True:
try:
msg = conn.recv()
print(f'Received: {msg}')
except EOFError:
break
if __name__ == '__main__':
parent_conn, child_conn = Pipe()
p1 = Process(target=sender, args=(child_conn,))
p2 = Process(target=receiver, args=(parent_conn,))
p1.start()
p2.start()
p1.join()
p2.join()
共享内存(Shared Memory)
from multiprocessing import Process, Value, Array
def increment_shared(shared_value, shared_array):
"""修改共享内存"""
with shared_value.get_lock(): # 使用锁保证线程安全
shared_value.value += 1
for i in range(len(shared_array)):
shared_array[i] = shared_array[i] * 2
if __name__ == '__main__':
# 'd' 表示 double 类型,'i' 表示 int 类型
shared_num = Value('d', 0.0)
shared_arr = Array('i', range(10))
processes = []
for _ in range(5):
p = Process(target=increment_shared, args=(shared_num, shared_arr))
processes.append(p)
p.start()
for p in processes:
p.join()
print(f'Final value: {shared_num.value}')
print(f'Final array: {list(shared_arr)}')
IPC 方法比较
| 方法 | 特点 | 性能 | 适用场景 |
|---|---|---|---|
| Queue | 线程/进程安全,FIFO | 中等 | 多生产者-消费者模式 |
| Pipe | 双向通信,简单快速 | 高 | 两个进程间简单通信 |
| Shared Memory | 直接内存访问 | 最高 | 大量数据共享 |
| Manager | 支持复杂对象 | 较低 | 跨网络共享对象 |
四、多线程编程(threading)
4.1 threading 模块概述
threading 模块是 Python 标准库中用于创建和管理线程的核心模块。虽然受到 GIL 的限制,但对于 I/O 密集型任务仍然非常有效。
特点:
- 线程共享同一进程的内存空间
- 创建和切换开销小于进程
- 受 GIL 限制,无法实现真正的并行计算
- 适合 I/O 密集型任务
4.2 Thread 类的使用
基本用法
import threading
import time
def worker(name, delay):
"""工作线程函数"""
print(f'{name} starting')
time.sleep(delay)
print(f'{name} finished after {delay} seconds')
# 创建线程
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(f'Thread-{i}', i+1))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
print('All threads completed')
继承 Thread 类
import threading
import time
class WorkerThread(threading.Thread):
def __init__(self, name, task_queue):
super().__init__()
self.name = name
self.task_queue = task_queue
self.daemon = True # 设置为守护线程
def run(self):
"""重写 run 方法"""
while True:
task = self.task_queue.get()
if task is None:
break
print(f'{self.name} processing task: {task}')
time.sleep(1)
self.task_queue.task_done()
# 使用示例
from queue import Queue
task_queue = Queue()
worker = WorkerThread('Worker-1', task_queue)
worker.start()
for i in range(5):
task_queue.put(f'Task-{i}')
task_queue.join() # 等待所有任务完成
task_queue.put(None) # 发送停止信号
worker.join()
4.3 线程同步机制
由于线程共享内存,需要同步机制防止数据竞争。
Lock(互斥锁)
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(100000):
lock.acquire() # 获取锁
try:
counter += 1
finally:
lock.release() # 释放锁
# 使用 with 语句更简洁
def increment_with_context():
global counter
for _ in range(100000):
with lock: # 自动获取和释放锁
counter += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f'Final counter: {counter}')
RLock(可重入锁)
import threading
class Counter:
def __init__(self):
self.value = 0
self.lock = threading.RLock() # 可重入锁
def increment(self):
with self.lock:
self.value += 1
self.double() # 可以在持有锁时再次获取
def double(self):
with self.lock: # 同一线程可以多次获取
self.value *= 2
counter = Counter()
counter.increment()
print(counter.value) # 输出: 2
Semaphore(信号量)
import threading
import time
# 限制同时访问资源的线程数
semaphore = threading.Semaphore(3)
def access_resource(name):
print(f'{name} waiting for resource')
with semaphore:
print(f'{name} acquired resource')
time.sleep(2)
print(f'{name} releasing resource')
threads = [threading.Thread(target=access_resource, args=(f'Thread-{i}',))
for i in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
Event(事件)
import threading
import time
event = threading.Event()
def waiter():
print('Waiting for event...')
event.wait() # 阻塞直到事件被设置
print('Event received!')
def setter():
print('Preparing to set event...')
time.sleep(3)
event.set() # 设置事件
print('Event set!')
t1 = threading.Thread(target=waiter)
t2 = threading.Thread(target=setter)
t1.start()
t2.start()
t1.join()
t2.join()
Condition(条件变量)
import threading
import time
condition = threading.Condition()
items = []
def consumer():
with condition:
while len(items) == 0:
print('Consumer waiting...')
condition.wait() # 等待通知
item = items.pop()
print(f'Consumer got: {item}')
def producer():
time.sleep(1)
with condition:
items.append('item')
print('Producer added item')
condition.notify() # 通知等待的线程
t1 = threading.Thread(target=consumer)
t2 = threading.Thread(target=producer)
t1.start()
t2.start()
t1.join()
t2.join()
4.4 线程池(ThreadPoolExecutor)
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def download_file(url):
"""模拟文件下载"""
print(f'Downloading {url}')
time.sleep(2)
return f'Content from {url}'
urls = [f'http://example.com/file{i}' for i in range(5)]
# 使用线程池
with ThreadPoolExecutor(max_workers=3) as executor:
# submit 方法:提交单个任务
futures = [executor.submit(download_file, url) for url in urls]
# 按完成顺序获取结果
for future in as_completed(futures):
result = future.result()
print(f'Got result: {result}')
# map 方法:批量处理
with ThreadPoolExecutor(max_workers=3) as executor:
results = executor.map(download_file, urls)
for result in results:
print(f'Got result: {result}')
五、异步编程(asyncio)
5.1 asyncio 模块概述
asyncio 是 Python 3.4 引入的异步 I/O 框架,通过协程和事件循环实现单线程并发。
核心概念:
- 协程(Coroutine):可以暂停和恢复的函数
- 事件循环(Event Loop):调度和执行协程的核心机制
- Future:表示异步操作的结果
- Task:对协程的封装,由事件循环调度
特点:
- 单线程执行,避免线程切换开销
- 适合高并发 I/O 密集型任务
- 使用 async/await 语法
- 不受 GIL 影响(因为是单线程)
5.2 协程的基本用法
定义和运行协程
import asyncio
async def hello():
"""协程函数使用 async def 定义"""
print('Hello')
await asyncio.sleep(1) # 暂停执行,释放控制权
print('World')
return 'Done'
# Python 3.7+ 推荐用法
result = asyncio.run(hello())
print(result)
# 低级 API(通常不推荐)
loop = asyncio.get_event_loop()
result = loop.run_until_complete(hello())
loop.close()
并发执行多个协程
import asyncio
import time
async def fetch_data(id, delay):
"""模拟异步获取数据"""
print(f'Fetching data {id}...')
await asyncio.sleep(delay)
return f'Data {id}'
async def main():
# 方法 1: 使用 gather 并发执行
results = await asyncio.gather(
fetch_data(1, 1),
fetch_data(2, 2),
fetch_data(3, 1)
)
print(f'Results: {results}')
# 方法 2: 使用 create_task
task1 = asyncio.create_task(fetch_data(4, 1))
task2 = asyncio.create_task(fetch_data(5, 2))
result1 = await task1
result2 = await task2
print(f'Task results: {result1}, {result2}')
start = time.time()
asyncio.run(main())
print(f'Total time: {time.time() - start:.2f}s')
5.3 事件循环
事件循环是 asyncio 的核心,负责调度和执行协程。
import asyncio
async def task1():
print('Task 1 starting')
await asyncio.sleep(2)
print('Task 1 done')
async def task2():
print('Task 2 starting')
await asyncio.sleep(1)
print('Task 2 done')
# 获取当前事件循环
async def main():
loop = asyncio.get_running_loop()
print(f'Event loop: {loop}')
# 在事件循环中调度任务
task1_handle = loop.create_task(task1())
task2_handle = loop.create_task(task2())
await task1_handle
await task2_handle
asyncio.run(main())
5.4 异步上下文管理器和迭代器
异步上下文管理器(async with)
import asyncio
class AsyncResource:
async def __aenter__(self):
print('Acquiring resource')
await asyncio.sleep(1)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print('Releasing resource')
await asyncio.sleep(1)
async def query(self):
print('Querying resource')
return 'result'
async def main():
async with AsyncResource() as resource:
result = await resource.query()
print(f'Got: {result}')
asyncio.run(main())
异步迭代器(async for)
import asyncio
class AsyncCounter:
def __init__(self, max_count):
self.max_count = max_count
self.count = 0
def __aiter__(self):
return self
async def __anext__(self):
if self.count >= self.max_count:
raise StopAsyncIteration
await asyncio.sleep(0.5)
self.count += 1
return self.count
async def main():
async for number in AsyncCounter(5):
print(f'Number: {number}')
asyncio.run(main())
5.5 异步 I/O 实战
异步 HTTP 请求(使用 aiohttp)
import asyncio
import aiohttp
async def fetch_url(session, url):
"""异步获取 URL"""
async with session.get(url) as response:
content = await response.text()
return len(content)
async def main():
urls = [
'https://www.python.org',
'https://www.github.com',
'https://www.stackoverflow.com'
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for url, size in zip(urls, results):
print(f'{url}: {size} bytes')
# asyncio.run(main()) # 需要安装 aiohttp
异步文件操作(使用 aiofiles)
import asyncio
import aiofiles
async def read_file(filename):
"""异步读取文件"""
async with aiofiles.open(filename, 'r') as f:
content = await f.read()
return content
async def write_file(filename, content):
"""异步写入文件"""
async with aiofiles.open(filename, 'w') as f:
await f.write(content)
async def main():
# 并发读取多个文件
files = ['file1.txt', 'file2.txt', 'file3.txt']
contents = await asyncio.gather(*[read_file(f) for f in files])
# 并发写入
await asyncio.gather(*[
write_file(f'output_{f}', content)
for f, content in zip(files, contents)
])
# asyncio.run(main()) # 需要安装 aiofiles
5.6 处理阻塞代码
asyncio 提供了在协程中运行阻塞代码的机制。
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def blocking_io():
"""阻塞式 I/O 操作"""
time.sleep(2)
return 'IO Result'
def cpu_intensive():
"""CPU 密集型任务"""
return sum(i * i for i in range(10**7))
async def main():
loop = asyncio.get_running_loop()
# 在线程池中运行阻塞 I/O
result = await loop.run_in_executor(
ThreadPoolExecutor(),
blocking_io
)
print(f'IO result: {result}')
# 在进程池中运行 CPU 密集型任务
result = await loop.run_in_executor(
ProcessPoolExecutor(),
cpu_intensive
)
print(f'CPU result: {result}')
asyncio.run(main())
六、并发模型选择指南
6.1 任务类型分析
6.2 性能对比实验
import time
import threading
import multiprocessing
import asyncio
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# CPU 密集型任务
def cpu_bound_task(n):
return sum(i * i for i in range(n))
# I/O 密集型任务
def io_bound_task(delay):
time.sleep(delay)
return f'Slept {delay}s'
async def async_io_task(delay):
await asyncio.sleep(delay)
return f'Slept {delay}s'
def benchmark_cpu_bound():
"""CPU 密集型任务性能测试"""
n = 10**7
tasks = 4
# 单线程
start = time.time()
for _ in range(tasks):
cpu_bound_task(n)
print(f'Sequential: {time.time() - start:.2f}s')
# 多线程(受 GIL 限制)
start = time.time()
with ThreadPoolExecutor(max_workers=4) as executor:
list(executor.map(cpu_bound_task, [n] * tasks))
print(f'Threading: {time.time() - start:.2f}s')
# 多进程(绕过 GIL)
start = time.time()
with ProcessPoolExecutor(max_workers=4) as executor:
list(executor.map(cpu_bound_task, [n] * tasks))
print(f'Multiprocessing: {time.time() - start:.2f}s')
def benchmark_io_bound():
"""I/O 密集型任务性能测试"""
delay = 1
tasks = 10
# 单线程
start = time.time()
for _ in range(tasks):
io_bound_task(delay)
print(f'Sequential: {time.time() - start:.2f}s')
# 多线程
start = time.time()
with ThreadPoolExecutor(max_workers=10) as executor:
list(executor.map(io_bound_task, [delay] * tasks))
print(f'Threading: {time.time() - start:.2f}s')
# asyncio
async def run_async():
await asyncio.gather(*[async_io_task(delay) for _ in range(tasks)])
start = time.time()
asyncio.run(run_async())
print(f'Asyncio: {time.time() - start:.2f}s')
if __name__ == '__main__':
print('=== CPU Bound Benchmark ===')
benchmark_cpu_bound()
print('\n=== I/O Bound Benchmark ===')
benchmark_io_bound()
6.3 最佳实践建议
选择决策树
- 任务是 CPU 密集型吗?
- 是 → 使用
multiprocessing - 否 → 继续判断
- 是 → 使用
- 任务是 I/O 密集型吗?
- 是 → 需要大量并发(1000+)?
- 是 → 使用
asyncio - 否 → 使用
threading
- 是 → 使用
- 否 → 考虑单线程优化
- 是 → 需要大量并发(1000+)?
- 需要混合处理吗?
- 是 →
asyncio+run_in_executor
- 是 →
常见场景推荐
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| Web 爬虫 | asyncio + aiohttp | 大量网络请求,高并发 |
| 图像处理 | multiprocessing | CPU 密集,需要并行 |
| 数据库操作 | threading | I/O 等待,中等并发 |
| API 服务器 | asyncio(FastAPI) | 高并发,异步I/O |
| 科学计算 | multiprocessing | CPU 密集,数值计算 |
| 文件转换 | multiprocessing + Pool | 批量处理,CPU 密集 |
七、常见陷阱与调试技巧
7.1 竞态条件(Race Condition)
import threading
# 错误示例:存在竞态条件
counter = 0
def increment():
global counter
for _ in range(100000):
counter += 1 # 非原子操作
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f'Expected: 1000000, Got: {counter}') # 结果不确定
# 正确做法:使用锁
counter = 0
lock = threading.Lock()
def safe_increment():
global counter
for _ in range(100000):
with lock:
counter += 1
threads = [threading.Thread(target=safe_increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f'With lock: {counter}') # 结果确定
7.2 死锁(Deadlock)
import threading
import time
# 错误示例:可能导致死锁
lock1 = threading.Lock()
lock2 = threading.Lock()
def thread1():
with lock1:
print('Thread 1 acquired lock1')
time.sleep(0.1)
with lock2: # 等待 lock2
print('Thread 1 acquired lock2')
def thread2():
with lock2:
print('Thread 2 acquired lock2')
time.sleep(0.1)
with lock1: # 等待 lock1
print('Thread 2 acquired lock1')
# 正确做法:统一加锁顺序
def safe_thread1():
with lock1:
with lock2:
print('Safe thread 1 executed')
def safe_thread2():
with lock1: # 相同的顺序
with lock2:
print('Safe thread 2 executed')
7.3 调试技巧
import logging
import asyncio
import threading
# 配置日志
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(threadName)s - %(message)s'
)
# 线程调试
def debug_thread():
thread_id = threading.get_ident()
logging.debug(f'Thread ID: {thread_id}')
# asyncio 调试
async def debug_coroutine():
# 启用调试模式
loop = asyncio.get_running_loop()
loop.set_debug(True)
# 记录慢回调
loop.slow_callback_duration = 0.1
await asyncio.sleep(0.2) # 会被记录为慢回调
# 使用 asyncio.run 的调试模式
# asyncio.run(debug_coroutine(), debug=True)
八、性能优化技巧
8.1 减少上下文切换
# 使用进程/线程池而非频繁创建
from concurrent.futures import ThreadPoolExecutor
# 不推荐:频繁创建线程
def bad_practice():
for _ in range(100):
t = threading.Thread(target=some_task)
t.start()
t.join()
# 推荐:使用线程池
def good_practice():
with ThreadPoolExecutor(max_workers=10) as executor:
executor.map(some_task, range(100))
8.2 选择合适的进程启动方法
import multiprocessing as mp
# spawn:更安全但启动慢
mp.set_start_method('spawn')
# fork:快速但可能有问题(仅 Unix)
mp.set_start_method('fork')
# forkserver:折中方案
mp.set_start_method('forkserver')
8.3 使用 numba 或 Cython 优化
# 使用 numba 加速 CPU 密集型代码
from numba import jit
@jit(nopython=True)
def fast_computation(n):
result = 0
for i in range(n):
result += i * i
return result
# 比纯 Python 快几十倍
result = fast_computation(10**7)
参考文献
- Real Python - What Is the Python Global Interpreter Lock (GIL)?
深入解释 GIL 的工作原理和影响
realpython.com/python-gil/ - Python Official Documentation - multiprocessing
官方文档,multiprocessing 模块的完整 API 参考
docs.python.org/3/library/m… - Python Official Documentation - threading
官方文档,threading 模块的详细说明
docs.python.org/3/library/t… - Python Official Documentation - asyncio
官方文档,asyncio 异步编程框架
docs.python.org/3/library/a… - PEP 703 - Making the Global Interpreter Lock Optional in CPython
移除 GIL 的官方提案
peps.python.org/pep-0703/ - Wikipedia - Global Interpreter Lock
GIL 的技术背景和跨语言对比
en.wikipedia.org/wiki/Global… - Real Python - Speed Up Your Python Program With Concurrency
实用的并发编程指南和性能优化建议
realpython.com/python-conc… - Super Fast Python - Multiprocessing Pool: The Complete Guide
进程池的详细使用教程
superfastpython.com/multiproces… - GeeksforGeeks - Multiprocessing in Python Set 2
进程间通信机制详解
www.geeksforgeeks.org/python/mult… - Stack Overflow - Multiprocessing vs Multithreading vs Asyncio
社区讨论三种并发模型的适用场景
stackoverflow.com/questions/2…