安全多进程追加写入文件需避免错乱,首选portalocker加锁或logging模块;次选消息队列中转;禁用直接open('a'),务必flush()和fsync()。

根本原因不是“Python报错”,而是多个进程同时打开、写入同一文件时,操作系统层面的I/O行为失控——没有锁,就没有顺序保障,内容交错、覆盖、丢失都算“正常”。
为什么open() + write()在多进程里会乱?
每个进程调用 open('log.txt', 'a') 时,得到的是**独立的文件对象和内核文件描述符**。即使路径相同,flock() 或 portalocker.lock() 也只作用于当前 fd,无法跨进程同步。
- Linux/macOS 默认用
fork,但子进程的 fd 是复制的,不共享锁状态 - Windows 用
spawn,连内存都不共享,更不可能共用一个 fd -
'a'模式虽保证每次write()追加到文件末尾,但“末尾位置”由内核维护,多个进程并发查询+写入,中间可能被其他进程插队
常见错误现象和对应原因
这些不是随机 bug,而是无锁并发的必然结果:
-
PermissionError: [WinError 32] 另一个程序正在使用此文件:Windows 下某进程正持有句柄(比如没 close 或日志 handler 没释放),其他进程 open 失败 - 日志内容粘连或错序,如
"procA: doneprocB: ok":两个进程几乎同时 write,内核缓冲区未 flush,落盘时机不可控 - 部分写入丢失:一个进程写到一半被调度出去,另一个进程覆盖了文件指针位置(尤其用
'w'模式时) -
portalocker.Lock不报错但内容仍混乱:底层flock()在 NFS、某些容器卷、或非 ext4/xfs 文件系统上不生效
怎么写才真正安全?
关键不是“选哪个库”,而是确保锁作用于同一内核对象,并强制落盘:
立即学习“Python免费学习笔记(深入)”;
- 统一用
portalocker.Lock('file.log', 'a')上下文管理器,别手写open+lock - 必须用
'a'模式(不截断),禁用'w'或'r+' - 每次写完立刻调用
f.flush();如果还担心,再加os.fsync(f.fileno()) - 避免在锁内做耗时操作(如网络请求、大计算),否则锁持有太久,拖慢整体吞吐
- 确认文件系统类型:
df -T .,如果不是ext4或xfs,flock类锁大概率失效
比文件锁更稳的替代方案
当业务逻辑本身需要读-改-写(比如 JSON 计数器),单靠文件锁解决不了竞态——它只防覆盖,不保语义一致:
- 用
Redis的SET key value NX EX 30做分布式锁,适合跨机器场景 - 用原子重命名做信号文件:
os.replace(temp_file, final_file),配合轮询判断 - 把日志发到队列(如
queue.Queue主进程消费,或ZeroMQ/Kafka),彻底避开文件竞争 - 对简单追加场景,用
ConcurrentRotatingFileHandler(注意 Windows 卡死问题)或nb_log,它们内部已封装锁+重试+缓冲策略
最常被忽略的一点:f.flush() 和 os.fsync() 不是可选项,是并发写入能被别的进程“看见”的唯一保障。没 flush,锁释放了,数据还在用户缓冲区里睡大觉。


















