windows用createfile+lockfileex实现排他锁,需设lockfile_exclusive_lock和lockfile_fail_immediately;unix应选fcntl而非flock,因其fd粒度、支持nfs且读写锁分离;跨平台封装须独占管理锁状态、校验权限并覆盖nfs等场景。

Windows下用CreateFile和LockFileEx实现排他锁
Windows原生不支持POSIX风格的flock,必须用WinAPI。关键不是打开文件,而是后续调用LockFileEx——它支持重叠I/O、超时和共享/独占模式。常见错误是忽略dwFlags参数:LOCKFILE_EXCLUSIVE_LOCK才表示写锁,LOCKFILE_FAIL_IMMEDIATELY能避免阻塞,否则默认会挂起线程。
实操建议:
- 用
GENERIC_READ | GENERIC_WRITE和FILE_SHARE_NONE打开文件,否则其他进程可能绕过锁 -
LockFileEx的lpOverlapped可传nullptr,但若设超时(如INFINITE),必须确保句柄是异步创建的(FILE_FLAG_OVERLAPPED) - 解锁必须显式调用
UnlockFileEx;进程退出时系统自动释放,但不可依赖——异常路径下容易漏掉
Linux/macOS用flock还是fcntl?选fcntl
flock简单但有严重缺陷:它基于进程而非文件描述符,且在NFS上基本失效;fcntl的F_SETLK才是可靠选择。它操作的是fd粒度,支持读写锁分离,且兼容NFS(只要服务端支持)。错误现象常是flock在容器或网络文件系统里“看起来生效但实际不互斥”。
实操建议:
- 用
open(..., O_RDWR)获取fd,O_RDONLY无法加写锁 -
struct flock中l_type设F_WRLCK或F_RDLCK,l_whence = SEEK_SET,l_start = l_len = 0锁整个文件 - 检查返回值:失败时
errno == EAGAIN表示被占用,errno == EDEADLK说明检测到死锁(极少见但需处理)
跨平台封装的关键:抽象出统一的锁生命周期
不能简单把两套API塞进一个类接口,核心矛盾在于语义差异:Windows锁绑定句柄,Unix锁绑定fd,而C++对象析构时机不确定。直接在析构函数里解锁风险很高——比如复制了std::shared_ptr指向该对象,多个副本析构时重复解锁会崩溃。
实操建议:
- 锁状态必须由RAII对象独占管理,内部用
std::unique_ptr持有资源(Windows用HANDLE,Unix用int),禁止拷贝,只允许移动 - 提供
try_lock()和lock_or_timeout(int ms),避免用户自己处理INFINITE或EAGAIN循环 - Windows下
CloseHandle前必须UnlockFileEx,Unix下close自动释放锁——这点差异必须封装干净,用户不应感知
实际使用中最容易被忽略的点:锁文件路径和权限
跨平台时路径分隔符(/ vs \)只是表象,真正坑的是权限模型。Windows对文件锁无权限要求,但Linux要求进程对文件有读/写权限才能调用fcntl;如果文件属主是root且权限为600,普通用户进程即使能open成功,fcntl也会返回EPERM。
实操建议:
- 构造锁对象前先做
access(path, W_OK)(Unix)或GetFileAttributes(Windows)校验可写性 - 不要锁目录——
flock和LockFileEx都不支持,有些实现会静默失败 - 测试必须覆盖NFS和本地ext4/APFS,尤其注意Docker容器内挂载卷时的锁行为是否一致
跨平台文件锁真正难的不是API调用,而是让锁在任意挂载方式、任意用户权限、任意进程生存周期下都保持语义一致。多数问题出在假设“锁住了就万事大吉”,其实释放时机、路径解析、错误传播链才是高频雷区。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











