flock和lockfileex不能混用,因二者语义不一致:flock基于文件描述符、自动释放、仅linux/macos支持;lockfileex基于句柄+偏移+长度、需显式解锁、windows专属,且互不可见。

为什么 flock 和 LockFileEx 不能直接混用
跨平台文件锁最常踩的坑,是以为 POSIX 的 flock 和 Windows 的 LockFileEx 行为一致。实际上:flock 是 advisory(建议性)且基于文件描述符,进程退出自动释放;LockFileEx 默认也是 advisory,但锁粒度到字节偏移、需显式 UnlockFileEx,且句柄关闭不等于释放锁(取决于 LOCKFILE_FAIL_IMMEDIATELY 等标志)。更关键的是:Linux 的 flock 锁无法被 Windows 进程感知,反之亦然——跨平台协作时若共用同一文件,必须统一抽象层,不能各自调原生 API。
用 fcntl + LockFileEx 实现语义对齐的关键点
要让 Linux 和 Windows 上的锁行为尽量一致(比如支持共享锁/独占锁、可重入判断、超时等待),核心是收敛到「字节范围锁」语义:fcntl 在 Linux 上可通过 F_SETLK/F_SETLKW 操作 struct flock 实现字节锁;Windows 则必须用 LockFileEx 配合 OVERLAPPED 支持超时。注意以下实操细节:
-
fcntl的F_GETLK只能查本进程是否会被阻塞,不能可靠检测其他进程锁状态;Windows 无等价接口,所以「尝试加锁失败即视为已被占用」是唯一可行判断逻辑 - Linux 下
fork后子进程继承文件描述符和锁,Windows 的句柄默认不继承,若需父子进程协同,得显式设bInheritHandle=TRUE - 超时控制:Linux
fcntl不支持超时,必须用select或poll配合非阻塞模式轮询;Windows 直接靠LockFileEx的dwMilliseconds参数 - 锁范围建议固定用文件开头(offset=0, length=1),避免因读写位置移动导致锁失效
如何避免多线程下 std::shared_ptr 析构引发的锁提前释放
常见错误是把文件描述符/句柄和锁状态全托管给 std::shared_ptr,看似自动管理,但问题在于:锁的生命周期应独立于对象引用计数。例如两个线程分别持有一个 shared_ptr<filelock></filelock>,其中一个析构时若直接 close/handle,可能意外释放另一个线程还在用的锁。
- 正确做法:锁资源(fd/handle)和锁状态(是否已加锁)分离;用 RAII 封装加锁/解锁动作,但底层 fd/handle 由外部传入或单独管理
- 加锁函数返回
bool而非异常——跨平台系统调用失败原因太多(磁盘满、权限不足、NFS 服务中断),异常反而掩盖真实路径分支 - 若需支持可重入,必须在类内部记录当前线程 ID(
std::this_thread::get_id()),且只允许同一线程重复加锁(需配合计数器),不能依赖系统级可重入锁(POSIX 和 Win32 均不提供)
Windows 上用 CreateFile 打开文件时必须设置的三个标志
很多跨平台封装在 Windows 下失败,根源在于 CreateFile 调用不当。即使后续用 LockFileEx,若打开方式不对,锁会静默失效:
-
dwShareMode必须包含FILE_SHARE_READ | FILE_SHARE_WRITE,否则其他进程连 open 都失败,根本谈不上锁竞争 -
dwCreationDisposition推荐用OPEN_EXISTING,避免CREATE_ALWAYS导致文件内容被清空而误伤业务逻辑 -
hTemplateFile设为NULL,且dwFlagsAndAttributes中**必须不含**FILE_ATTRIBUTE_HIDDEN或FILE_ATTRIBUTE_SYSTEM——某些杀毒软件会拦截对这类文件的LockFileEx调用,错误码返回ERROR_ACCESS_DENIED而非预期的锁冲突
跨平台文件锁真正难的不是语法,而是不同系统对「锁存在」的定义差异——Linux 认为锁绑定 fd,Windows 认为锁绑定 handle+offset+length+flags。只要没在设计初期就明确锁的语义边界,后期补丁只会越来越重。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











