
本文介绍在多进程场景下避免文件竞态条件的核心方法:使用进程间同步机制(如 multiprocessing.Lock)或改用线程模型配合 threading.Lock,确保对共享 .pkl 文件的读写操作原子、有序、无冲突。
本文介绍在多进程场景下避免文件竞态条件的核心方法:使用进程间同步机制(如 `multiprocessing.lock`)或改用线程模型配合 `threading.lock`,确保对共享 `.pkl` 文件的读写操作原子、有序、无冲突。
在 Python 中使用 multiprocessing 并发处理多个任务时,若多个子进程需反复读写同一组 .pkl 文件(例如加载字典 → 计算更新 → 保存回原文件),极易引发竞态条件(race condition):一个进程正在写入文件时,另一进程可能正在读取,导致反序列化失败、数据损坏或静默错误。根本原因在于:threading.Lock 无法跨进程生效,而默认的文件 I/O 操作不具备原子性。
✅ 正确解法不是“绕开同步”,而是显式引入进程级互斥锁。推荐使用 multiprocessing.Lock(而非 threading.Lock),因为它专为跨进程同步设计:
import multiprocessing as mp
import pickle
import os
def process_task(task_id, pkl_path, lock):
# 每次读写前必须获取锁
with lock:
# 安全读取
if os.path.exists(pkl_path):
with open(pkl_path, 'rb') as f:
data = pickle.load(f)
else:
data = {}
# 执行业务逻辑(示例:添加新键值)
data[f'process_{task_id}'] = {'timestamp': task_id * 100}
# 安全写入(覆盖原文件)
with open(pkl_path, 'wb') as f:
pickle.dump(data, f)
print(f"Process {task_id}: updated {pkl_path}")
if __name__ == '__main__':
pkl_file = 'shared_data.pkl'
lock = mp.Lock() # 进程安全锁
processes = []
for i in range(4):
p = mp.Process(target=process_task, args=(i, pkl_file, lock))
processes.append(p)
p.start()
for p in processes:
p.join()
# 验证结果
with open(pkl_file, 'rb') as f:
final_data = pickle.load(f)
print("Final data:", final_data)⚠️ 关键注意事项:
- 切勿混用 threading.Lock 和 multiprocessing.Process:threading.Lock 仅在线程间有效,对不同进程完全无效;
- 锁的粒度要合理:将整个读–改–写流程包裹在 with lock: 中,确保原子性;避免在锁内执行耗时操作(如网络请求),否则严重降低并发效率;
- 考虑替代方案:若 I/O 占比高、CPU 密集度低,改用 threading + threading.Lock 更轻量(因无需进程启动开销);但若涉及大量 CPU 计算,仍应坚持 multiprocessing + multiprocessing.Lock;
- 进阶选择:对高吞吐场景,可采用 multiprocessing.Manager().dict() 或 Redis 等外部存储解耦状态,避免文件争抢;
- 文件系统级锁(如 fcntl)虽可行,但跨平台兼容性差(Windows 不支持),不推荐作为首选。
总结:多进程共享文件写入的本质是资源互斥问题,唯一可靠方案是显式加锁——且必须选用进程感知的同步原语。multiprocessing.Lock 是标准库中最简洁、最健壮的选择,配合 with 语句可确保异常安全释放,是构建稳定并发数据管道的基石。
立即学习“Python免费学习笔记(深入)”;


















