std::fstream 不提供文件锁管理,需自行封装RAII类调用系统API实现:Windows用CreateFile+LockFileEx,Linux用open+flock;锁必须与句柄/fd绑定,析构时显式解锁并关闭资源。

std::fstream 本身不管理文件锁,RAII 锁必须自己封装
标准 C++ 的 std::fstream 打开文件时不会自动加排他锁(如 Windows 的 LOCKFILE_EXCLUSIVE_LOCK 或 POSIX 的 flock()),更不会在析构时释放。所谓“通过 RAII 自动释放文件锁”,其实是你写一个 RAII 类来包装底层系统调用——std::fstream 只负责 I/O,锁是另一层责任。
常见错误现象:std::ofstream f("data.txt"); 看似“打开即锁定”,其实只是让操作系统分配句柄,其他进程仍可同时读写该文件,Windows 下甚至可能报错 Access is denied,Linux 下则完全无保护。
- Windows 推荐用
CreateFile()+LockFileEx(),再用CloseHandle()自动释放 - Linux/macOS 用
open()+flock(),锁随 fd 关闭自动释放(但注意:std::fstream::close()不等于close()系统调用) - 别试图把
flock()套在std::fstream::rdbuf()->fd()上——该 fd 可能已被库内部关闭或复用,行为未定义
Windows 下用 RAII 封装 CreateFile + LockFileEx
关键点:锁的生命周期必须和句柄(HANDLE)绑定,且锁必须在句柄关闭前显式释放(否则可能阻塞其他进程)。RAII 类应在构造时加锁、析构时解锁 + 关闭句柄。
示例核心逻辑:
立即学习“C++免费学习笔记(深入)”;
class FileLockGuard {
HANDLE h_;
public:
FileLockGuard(const char* path) : h_(INVALID_HANDLE_VALUE) {
h_ = CreateFileA(path, GENERIC_READ | GENERIC_WRITE,
0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h_ == INVALID_HANDLE_VALUE) return;
<pre class="brush:php;toolbar:false;"> OVERLAPPED ol = {};
if (!LockFileEx(h_, LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &ol)) {
CloseHandle(h_);
h_ = INVALID_HANDLE_VALUE;
}
}
~FileLockGuard() {
if (h_ != INVALID_HANDLE_VALUE) {
UnlockFileEx(h_, 0, 1, 0, nullptr);
CloseHandle(h_);
}
}
operator bool() const { return h_ != INVALID_HANDLE_VALUE; }};
使用时:FileLockGuard lock("config.dat"); if (!lock) { /* 失败 */ }。注意:它不提供读写接口,只管锁;后续仍要用 std::fstream 或 WriteFile() 操作同一路径——但要确保没别的进程/线程在碰这个文件。
Linux 下用 RAII 封装 open + flock
POSIX 的 flock() 是建议性锁,依赖所有参与者主动检查,但它和 fd 绑定紧密:只要持有该 fd 的进程存在,锁就有效;fd 关闭即自动释放。所以 RAII 类只需确保 fd 在析构时关闭。
陷阱比 Windows 更隐蔽:
-
std::fstream构造时若传入已打开的 fd(如用std::filebuf::open(int fd, ...)),其内部可能在析构时调用close(fd),导致锁提前丢失 - 不要混用
flock()和fcntl(F_WRLCK),它们互不感知 - 多线程中,
flock()是进程级锁,不是线程级——同一进程内多个线程加锁会成功,起不到互斥作用
安全做法是全程绕过 std::fstream,用 read()/write() 直接操作 fd:
class FileLockGuard {
int fd_;
public:
FileLockGuard(const char* path) : fd_(-1) {
fd_ = open(path, O_RDWR | O_CREAT, 0644);
if (fd_ == -1) return;
if (flock(fd_, LOCK_EX | LOCK_NB) == -1) {
close(fd_);
fd_ = -1;
}
}
<pre class="brush:php;toolbar:false;">~FileLockGuard() {
if (fd_ != -1) {
flock(fd_, LOCK_UN);
close(fd_);
}
}
int fd() const { return fd_; }};
之后用 write(lock.fd(), buf, len) 写入,避免任何 std::fstream 干预 fd 生命周期。
跨平台?别硬凑,按部署环境选方案
没有标准 C++ 接口能统一 Windows 和 Linux 文件锁语义。强行封装一层抽象(比如定义 ILock 接口)只会掩盖差异,增加误用风险。实际项目中:
- Windows 服务程序:用
CreateFile+LockFileEx,注意LOCKFILE_FAIL_IMMEDIATELY避免阻塞 - Linux 后台工具:用
flock()+open(),配合LOCK_NB做非阻塞尝试 - 如果必须用
std::fstream,那就放弃排他锁,改用原子重命名(rename("tmp", "final"))+ 临时文件,这是更健壮的替代方案
最常被忽略的一点:文件锁不能防止进程崩溃后残留锁。Windows 下 LockFileEx 的锁在句柄关闭时自动释放,但若进程死锁或异常终止,句柄可能没机会关闭;Linux 下 flock 会随进程终止自动清理——这点反而是 Linux 更可靠。



















