std::shared_mutex无法解决跨进程文件冲突,因其仅作用于单进程内内存对象;跨进程需用flock(linux)或lockfileex(windows)等系统级文件锁,且须注意advisory锁的协作性、生命周期绑定fd、nfs限制及写覆盖等陷阱。

直接用 std::shared_mutex 无法解决跨进程文件冲突——它只管线程,不管进程。真要防多进程同时改同一个文件,得靠操作系统级文件锁,比如 flock(Linux)或 LockFileEx(Windows)。
为什么 std::shared_mutex 对文件无效
std::shared_mutex 锁的是内存里的对象,不是磁盘上的文件。两个进程各自加载一份程序副本,它们的 std::shared_mutex 实例完全独立,互不感知。哪怕你把 mutex 存在 mmap 区域里,也不自动具备跨进程同步语义;必须显式映射到共享内存段并初始化为进程间共享,但这和“锁文件”是两回事。
- 它只在单进程多线程场景下生效,
lock_shared()允许多个线程并发读,lock()阻塞所有其他线程 - 对
std::fstream或FILE*的操作本身不是原子的,即使加了shared_mutex,write() 系统调用仍可能被另一个进程的 write() 覆盖 - 如果你试图用它保护一个全局
std::ofstream对象,那只是防止本进程内多个线程乱序写,不防外部进程
flock 是什么,以及它怎么用才不踩坑
flock 是 Linux/Unix 上最常用的 advisory 锁机制,轻量、基于文件描述符,但行为容易误判。
- 它不是强制锁:依赖所有参与者主动调用
flock(fd, LOCK_EX),不调就等于没锁 - 锁生命周期绑定 fd:只要
close(fd)或进程崩溃,锁立刻释放——不是“锁住文件路径”,所以别 open 一次长期 hold 住 fd 当守护锁用 - 子进程默认继承 fd 和锁状态,但
fork后父子对同一 fd 的flock是独立的;若需父子互斥,得各自重新flock - NFS 文件系统上不可靠,某些挂载方式会静默失败,建议检查
errno == ENOTSUP并 fallback 到其他机制(如 pidfile + signal 检查)
典型安全用法:
int fd = open("config.json", O_RDWR);
if (fd == -1) { /* handle error */ }
struct flock fl = {0};
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0; // 锁整个文件
if (fcntl(fd, F_SETLKW, &fl) == -1) { /* 失败,可能是被占用了 */ }
// 此时可安全 read/write
// ...操作...
fl.l_type = F_UNLCK;
fcntl(fd, F_SETLK, &fl); // 非阻塞解锁
close(fd); // 关闭即自动释放,双重保险
Windows 下 LockFileEx 比 LockFile 更实用
LockFile 要求传入精确字节范围,且不支持超时;LockFileEx 支持重叠 I/O 和毫秒级超时,更适合生产环境。
- 想锁整个文件?先
GetFileSize,再传0和实际大小;别传0和MAXDWORD,Windows 会截断且大文件可能报ERROR_NOT_ENOUGH_MEMORY -
LOCKFILE_EXCLUSIVE_LOCK表示写锁;没有原生共享锁,如需“多读一写”,得自己约定一个 header 字节(如 offset=0, len=1)作为写标志位 - 返回
FALSE不一定失败:GetLastError()是ERROR_IO_PENDING表示异步等待中,ERROR_LOCK_VIOLATION才是已被占用 - 必须配
OVERLAPPED结构体,且hEvent字段设为非 NULL 才能触发完成通知
跨平台封装的关键避坑点
别写 #ifdef WIN32 + #ifdef __linux__ 两套裸逻辑——平台差异远不止函数名。
-
flock允许同一进程多次LOCK_EX(可重入),LockFile对同一句柄重复调用直接失败 -
flock支持LOCK_SH(共享锁),LockFile根本没有共享语义,只能模拟 - Linux 下关闭 fd 即释放锁,Windows 下必须显式
UnlockFile,否则锁残留 - 统一接口如
bool file_lock(int fd, bool exclusive, int timeout_ms)很有必要,内部 dispatch 才可控
最容易被忽略的一点:锁只保证“写操作不交错”,不保证“写入内容逻辑正确”。比如两个进程都读取 JSON 文件 → 修改某个字段 → 写回全量,即使加了排他锁,仍可能产生 A 覆盖 B 的修改(lost update)。这种场景需要更上层的乐观锁或版本号控制,文件锁只是第一道防线。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











