std::mutex仅限进程内同步,无法防止多进程写同一文件的冲突,因各进程拥有独立文件描述符,导致内容交错、覆盖或截断。

直接用 std::ofstream 加 std::mutex 只能防同进程内多线程冲突,对多进程写同一个文件完全无效——错乱、覆盖、截断是必然结果,不是概率问题。
为什么 std::mutex 不能解决多进程文件写冲突
std::mutex 是进程内同步原语,它锁不住操作系统层面的文件描述符。两个进程各自打开 "log.txt",哪怕都加了 std::mutex,底层仍是两个独立的 fd,write() 调用会竞争文件偏移量,导致内容交错或丢失。
- 典型现象:
write()返回成功但内容被覆盖;std::ios_base::failure异常;文件末尾出现乱码或空洞 - 根本原因:Linux/macOS 的
open()默认不带O_APPEND时,lseek() + write()非原子;Windows 下无类似内核级追加保障 - 注意:
std::ios::app模式只影响 C++ 流的缓冲行为,不等价于系统级O_APPEND,也不能跨进程同步
Linux/macOS 下用 flock() 实现进程级写锁
必须绕过 std::ofstream,用系统调用获取 fd 后显式加锁。因为 flock() 锁的是 fd,而 std::ofstream 不暴露 fd 接口。
- 先
open("log.txt", O_WRONLY | O_APPEND | O_CREAT, 0644)获取 fd - 再
flock(fd, LOCK_EX)阻塞加写锁(所有进程都得调用才生效) - 然后
write(fd, ...)+fsync(fd)确保落盘 - 最后
flock(fd, LOCK_UN)+close(fd) - 关键点:
flock()是建议锁,不强制拦截 open() 或 write(),所有写方必须主动参与加锁逻辑
Windows 下 LockFileEx() 的正确调用路径
不能对 std::ofstream 内部句柄直接调 LockFileEx(),它要求句柄以 FILE_FLAG_OVERLAPPED 创建,而标准流默认不是。
- 正确做法:用
CreateFile()显式传入FILE_FLAG_OVERLAPPED和GENERIC_WRITE - 拿到
HANDLE后调LockFileEx()(注意dwFlags设为LOCKFILE_EXCLUSIVE_LOCK) - 用
_open_osfhandle()转成 CRT fd,再用_fdopen()包装成FILE*,最后构造std::ostream - 常见误用:
GetLastError()返回ERROR_NOT_SUPPORTED就是没按这个路径走
跨进程安全写文件更实用的替代方案
自己封装 flock() + LockFileEx() 看似通用,但容易漏掉信号处理、锁释放时机、错误恢复等细节,线上出问题很难 debug。
- 推荐用“临时文件 + rename”:每个进程写独立命名的临时文件(如
log_12345.tmp),完成后rename()到目标名 —— Linux 下rename()是原子的,Windows 需用MoveFileEx()配合MOVEFILE_REPLACE_EXISTING - 或者用命名互斥体(
CreateMutex()/pthread_mutex_twithPTHREAD_PROCESS_SHARED)控制写入权,但写操作本身仍需保证原子性(比如单次write()不超PIPE_BUF) - 最省心的是换日志库:
g3log、spdlog(启用async_logger)已内置进程安全策略,不用自己啃系统 API
真正难的不是加锁,而是所有参与者是否遵循同一套锁协议——漏一个进程,整个锁就形同虚设。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











