Python多进程写入冲突的本质是操作系统不自动串行化跨进程文件写入,需依赖flock()(Linux/macOS)或LockFileEx()(Windows)等系统级锁;threading.Lock无效,临时文件+os.replace()不适用于追加场景。

Python多进程写入冲突的本质是什么
多个进程同时打开同一个文件并调用 write(),操作系统不会自动串行化写入操作。即使每个进程都用 with open(...) 上下文管理,也无法阻止内核层面的竞态——比如两个进程几乎同时调用 lseek() 到文件末尾再写入,结果就是内容覆盖或错位。
关键点在于:flock()(POSIX)或 LockFileEx()(Windows)这类系统级锁,才是唯一能跨进程生效的同步机制;Python标准库的 threading.Lock 只作用于单个进程内的线程,对多进程完全无效。
用 fcntl.flock() 实现可靠文件锁(Linux/macOS)
这是最轻量、最直接的方式,依赖底层 flock() 系统调用,支持共享锁/独占锁,且会随进程退出自动释放(避免死锁)。
- 必须在打开文件后、任何读写前调用
flock(),且文件描述符需保持打开状态 - 使用
fcntl.LOCK_EX获取排他锁(写入场景),fcntl.LOCK_NB可设为非阻塞模式 - 不要对已关闭的文件描述符加锁,否则抛
OSError: [Errno 9] Bad file descriptor - 注意:NFS 文件系统可能不支持
flock(),此时需换用基于临时文件的方案
import fcntl import osdef append_to_log(path, content): with open(path, "a") as f: fcntl.flock(f.fileno(), fcntl.LOCK_EX) try: f.write(content + "\n") f.flush() # 确保内容落盘 finally: fcntl.flock(f.fileno(), fcntl.LOCK_UN) # 显式解锁更安全
Windows 下用 mmap + msvcrt.locking() 不靠谱,改用 portalocker
Windows 原生不支持 flock(),而 msvcrt.locking() 仅作用于文件片段、接口晦涩,且容易因缓冲区未刷新导致行为异常。实际项目中应直接使用成熟封装库。
立即学习“Python免费学习笔记(深入)”;
-
portalocker底层调用LockFileEx(),跨平台接口统一,支持超时和上下文管理 - 安装:
pip install portalocker - 若进程被强制 kill(如
kill -9),锁仍能被系统回收,这点比自建临时文件标记更可靠
import portalockerdef safe_write(path, content): with open(path, "a") as f: portalocker.lock(f, portalocker.LOCK_EX) try: f.write(content + "\n") f.flush() finally: portalocker.unlock(f)
为什么不用临时文件 + os.replace() 替代锁
该方案(写入临时文件后原子重命名)适用于「整文件替换」场景,但不适用于「追加日志」或「增量更新」——因为每次都要读取原文件、合并、再全量写回,IO放大严重,且无法保证多进程间追加顺序。
- 适合:配置文件热更新、生成静态报告等「全量写入」任务
- 不适合:日志追加、计数器累加、CSV逐行写入等「高频小写入」场景
- 注意:
os.replace()在同文件系统内是原子的,跨文件系统会退化为复制+删除,失去原子性
真正难处理的是「既要并发安全,又要保持追加语义」——这时候绕不开系统级文件锁,没有更简单的替代路径。










