3、并行计算基础:多线程与多进程概念、Python中的threading与multiprocessing模块、GIL对并行的影响。
在DRC脚本自动化进阶中,当面对大规模版图数据和多条复杂规则时,串行验证的执行时间往往难以接受。并行计算是提升吞吐量的核心手段。本章将厘清多线程与多进程的本质区别,剖析Python中threading与multiprocessing模块的适用场景,并重点解释全局解释器锁(GIL)对并行任务的实际影响,为后续构建多规则并行验证引擎奠定理论基础。
3.1 多线程与多进程:概念与对比
并行计算的核心在于将任务拆解为可同时执行的子任务。多线程与多进程是实现并行的两种基本模型,它们在资源开销、数据共享和适用场景上存在显著差异。
| 对比维度 | 多线程 (Multi-threading) | 多进程 (Multi-processing) |
|---|---|---|
| 资源开销 | 轻量级,线程共享进程内存空间,创建和切换开销小 | 重量级,每个进程拥有独立内存空间,创建和切换开销大 |
| 数据共享 | 天然共享全局变量,但需加锁避免竞态条件 | 默认不共享,需通过IPC(队列、管道、共享内存)通信 |
| GIL影响 | 受GIL限制,CPU密集型任务无法真正并行 | 不受GIL影响,每个进程拥有独立解释器,可充分利用多核 |
| 适用场景 | I/O密集型任务(文件读写、网络请求、数据库查询) | CPU密集型任务(几何运算、规则检查、数据解析) |
| 健壮性 | 一个线程崩溃可能导致整个进程退出 | 进程间隔离,单个进程崩溃不影响其他进程 |
DRC场景提示: 在DRC验证中,规则检查本身是CPU密集型(如多边形布尔运算、间距计算),应优先考虑多进程。而文件I/O与日志输出属于I/O密集型,可结合多线程优化。
3.2 Python中的threading模块
threading模块提供了基于线程的并行接口。它通过创建Thread对象并调用start()来启动线程,适合处理I/O等待任务。
import threading
import time
def drc_log_writer(log_id, duration):
"""模拟DRC日志写入的I/O操作"""
print(f"[线程-{log_id}] 开始写入日志...")
time.sleep(duration) # 模拟I/O等待
print(f"[线程-{log_id}] 日志写入完成")
threads = []
for i in range(4):
t = threading.Thread(target=drc_log_writer, args=(i, 2))
threads.append(t)
t.start()
for t in threads:
t.join()
print("所有日志线程执行完毕")
上述代码中,4个日志写入线程在等待I/O时主动释放GIL,使得其他线程可以继续执行,从而在I/O密集型场景下显著提升效率。
3.3 Python中的multiprocessing模块
multiprocessing模块通过创建子进程来绕过GIL限制,实现真正的CPU并行。它提供了Process、Pool、Queue等核心组件。
from multiprocessing import Process, Queue
import os
def drc_rule_check(rule_id, result_queue):
"""模拟DRC规则检查的CPU密集型计算"""
print(f"[进程-{rule_id}] PID: {os.getpid()} 开始检查...")
# 模拟复杂几何运算
result = sum(i * i for i in range(10**6))
result_queue.put({rule_id: result})
print(f"[进程-{rule_id}] 检查完成")
if __name__ == "__main__":
queue = Queue()
processes = []
for i in range(4):
p = Process(target=drc_rule_check, args=(i, queue))
processes.append(p)
p.start()
for p in processes:
p.join()
while not queue.empty():
print(f"结果: {queue.get()}")
关键注意: 使用
multiprocessing时,必须将进程启动代码放在if __name__ == "__main__":保护块内,否则在Windows系统上会导致无限递归创建子进程。
3.4 GIL对并行的影响
全局解释器锁(GIL)是CPython解释器的一个互斥锁,它确保同一时刻只有一个线程执行Python字节码。这意味着在多线程环境下,即使有多个CPU核心,也无法同时执行多个线程的Python代码。
3.5 并行模型选择策略
在DRC脚本自动化中,根据任务特性选择合适的并行模型至关重要。以下策略可帮助开发者做出决策:
- 纯CPU密集型规则检查:使用
multiprocessing.Pool,将版图分区或规则分组后分配给多个进程并行执行。 - 大量文件I/O的预处理/后处理:使用
threading或concurrent.futures.ThreadPoolExecutor,在等待磁盘I/O时并发处理其他任务。 - 混合工作流:采用“多进程计算 + 多线程日志/结果收集”的架构,进程负责计算,线程负责非阻塞输出。
- 数据共享需求:多进程间通过
multiprocessing.Queue或Manager传递数据,避免直接共享内存带来的同步复杂性。