flock无法跨平台直接使用,因Linux/macOS依赖文件描述符的advisory锁,而Windows需HANDLE和LockFileEx且语义差异大;RAII类须按平台分别实现构造(open+flock或CreateFile+LockFileEx)与析构(close或UnlockFileEx),禁用拷贝、支持移动,并统一错误码映射为std::system_error。

为什么 flock 不能直接跨平台用
Linux/macOS 的 flock 是基于文件描述符的 advisory 锁,Windows 根本不支持这个系统调用;而 Windows 的 LockFileEx 要求传入 HANDLE,且语义(如重叠锁、字节范围锁)和 POSIX 差异明显。直接封装一层抽象会掩盖行为差异,比如:同一文件在 Linux 上用 flock 加锁后,另一个进程用 open+flock 可以检测到冲突;但在 Windows 上若没正确设置 CREATE_ALWAYS 或共享模式,可能连文件都打不开,更别说锁了。
-
flock在 macOS 和 Linux 行为基本一致,但不支持字节范围锁 - Windows 的
LockFileEx必须配合FILE_SHARE_READ | FILE_SHARE_WRITE打开句柄,否则其他进程 open 失败 - Windows 上对已映射内存(
CreateFileMapping)的文件加锁无效,必须用原始文件句柄
如何设计 RAII 类的构造与析构逻辑
核心是「锁在构造时获取,失败则抛异常;析构时无条件释放」,但释放动作必须区分平台——Linux/macOS 调用 close(因为 flock 锁随 fd 关闭自动释放),Windows 则必须显式调用 UnlockFileEx。不能依赖析构顺序或静态对象生命周期,尤其避免在 std::atexit 中清理,因为锁状态可能早已失效。
- 构造函数中:Linux 用
open+flock(LOCK_EX | LOCK_NB);Windows 用CreateFile(带FILE_SHARE_READ | FILE_SHARE_WRITE)+LockFileEx - 析构函数中:Linux 调用
close(m_fd);Windows 调用UnlockFileEx(m_hFile, 0, 0, 0, &ov),忽略返回值(避免析构中 throw) - 禁用拷贝,只允许移动:声明
FileLock(const FileLock&) = delete;,实现移动构造并置空源对象的m_fd或m_hFile
跨平台路径与错误处理怎么统一
路径分隔符不是重点,真正麻烦的是错误码映射:flock 失败返回 errno == EWOULDBLOCK,而 LockFileEx 失败调用 GetLastError() 得到 ERROR_LOCK_VIOLATION。RAII 类内部应统一转为 std::system_error,用自定义 std::error_category 封装,这样上层只需写 catch (const std::system_error& e) { if (e.code().category() == file_lock_category()) ... }。
- Linux 构造失败:检查
errno,若为EWOULDBLOCK或EAGAIN,转为std::errc::device_or_resource_busy - Windows 构造失败:检查
GetLastError(),ERROR_LOCK_VIOLATION→ 同样映射到std::errc::device_or_resource_busy - 不要吞掉
EBADF或INVALID_HANDLE_VALUE这类严重错误,应转为std::errc::bad_file_descriptor
实际使用时最容易被忽略的点
很多人以为只要封装好 RAII 就万事大吉,结果在 Windows 上遇到死锁或假成功:比如用 std::fstream 打开文件后再传给锁类,此时 fstream 内部的 HANDLE 没有共享标志,LockFileEx 会失败但被忽略;或者多个线程用同一个 FileLock 实例反复构造析构,而没注意移动语义是否生效,导致 double-close 或 double-unlock。
立即学习“C++免费学习笔记(深入)”;
- 锁类必须独占管理文件打开逻辑,禁止接受外部传入的
int fd或HANDLE - 测试时务必覆盖「同一进程内多次构造同一路径」和「不同进程竞争同一文件」两种场景
- Windows 下若文件被其他程序以独占方式打开(如记事本编辑中),你的
CreateFile就会失败,这不是锁的问题,而是打开阶段就该处理的前置条件
跨平台文件锁从来不是“写一次跑三家”,而是每种系统都要直面它的原生约束。RAII 只是帮你把资源生命周期管住,但锁的语义鸿沟,得靠实测和错误码翻译来填。


















