Python多进程写入冲突的本质是操作系统不自动串行化跨进程文件写入,需依赖flock()(Linux/macOS)或LockFileEx()(Windows)等系统级锁;threading.Lock无效,临时文件+os.replace()不适用于追加场景。
多个进程同时打开同一个文件并调用 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
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) # 显式解锁更安全
mmap + msvcrt.locking() 不靠谱,改用 portalocker
Windows 原生不支持 flock(),而 msvcrt.locking() 仅作用于文件片段、接口晦涩,且容易因缓冲区未刷新导致行为异常。实际项目中应直接使用成熟封装库。
portalocker 底层调用 LockFileEx(),跨平台接口统一,支持超时和上下文管理pip install portalocker
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放大严重,且无法保证多进程间追加顺序。
os.replace() 在同文件系统内是原子的,跨文件系统会退化为复制+删除,失去原子性真正难处理的是「既要并发安全,又要保持追加语义」——这时候绕不开系统级文件锁,没有更简单的替代路径。