std::ofstream本身不提供跨进程文件锁,仅靠std::mutex无法防止多进程写冲突;必须使用操作系统级锁(如Linux的flock()或Windows的LockFileEx())或无锁方案(如独立文件+合并)。

std::ofstream 本身不提供跨进程文件锁
直接用 std::ofstream 打开文件并写入,哪怕加了 std::mutex,也只能防止同进程内多线程冲突,对其他进程完全无效。常见现象是:两个进程同时写同一个日志文件,内容错乱、覆盖或截断——这不是线程问题,是文件系统级别的竞态。
真正要解决,得靠操作系统提供的 advisory lock(建议性锁)或 mandatory lock(强制锁),C++ 标准库不封装这些,必须调用底层 API:
- Linux/macOS:用
flock()或fcntl()(推荐flock(),简单且支持进程级阻塞) - Windows:用
CreateFile()+LockFileEx(),或更轻量的LockFile()
flock() 在 Linux 下怎么安全配合 std::ofstream 使用
flock() 锁的是文件描述符(fd),而 std::ofstream 不暴露 fd。所以不能先开流再锁,得反着来:先用 open() 获取 fd → 加 flock() → 再用 fdopen() 包装成 FILE* → 最后用 std::ostream 构造函数绑定(需 C++11 起支持 std::ostream(std::streambuf*))。
更实用的做法是绕过 std::ofstream,直接用 write() + fsync(),或者封装一个带锁的写函数:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
int fd = open("log.txt", O_WRONLY | O_APPEND | O_CREAT, 0644);
if (fd == -1) { /* error */ }
if (flock(fd, LOCK_EX) == -1) { /* error */ }
write(fd, "hello\n", 6);
fsync(fd); // 防止缓存未落盘
flock(fd, LOCK_UN);
close(fd);
注意:flock() 是建议锁,所有参与者都得主动调用才生效;它不阻止 open(),只在你显式加锁时阻塞。
Windows 下 LockFileEx() 的典型误用点
容易直接对 std::ofstream 的句柄调用 LockFileEx(),但 C++ 标准流默认以非重叠(non-overlapped)方式打开文件,而 LockFileEx() 要求句柄是 FILE_FLAG_OVERLAPPED 创建的——否则调用失败,GetLastError() 返回 ERROR_NOT_SUPPORTED。
正确路径是:用 CreateFile() 显式指定标志 → 得到 HANDLE → 调用 LockFileEx() → 用 _open_osfhandle() 转成 CRT fd → 再用 _fdopen() 套成 FILE*。若坚持用 std::ofstream,只能放弃锁,改用命名互斥体(CreateMutex())做进程间同步,但它不锁文件,只保“同一时间只有一个写者”,仍需自行保证写操作原子性(比如写临时文件再 rename)。
跨平台方案为什么别自己封装 flock + LockFileEx
表面看可以写个 ScopedFileLock 类,Linux 走 flock(),Windows 走 LockFileEx(),但实际坑很深:
-
flock()锁随 fd 关闭自动释放,LockFileEx()必须配对调用UnlockFileEx(),忘了就死锁 -
flock()支持共享锁(LOCK_SH),Windows 文件锁只有独占模式 - Linux 的
flock()不支持锁文件某一段,Windows 的LockFileEx()支持偏移+长度,语义不等价 - 容器环境或 NFS 挂载点下,
flock()可能完全失效(NFSv3 默认不支持)
真要跨平台,优先考虑无锁设计:每个线程/进程写独立文件(如加 pid 或 timestamp 后缀),再由外部工具合并;或者用成熟的日志库(如 g3log、spdlog),它们已处理好锁粒度、缓冲刷新和异常安全。

















