std::fstream不自动加锁,因文件锁属操作系统层面且非RAII职责;需自封装类在构造中open+flock、析构中funlock+close,并禁用拷贝以确保异常安全与锁正确性。

RAII 能自动管理文件流的开启与锁定,但标准 std::fstream 本身不处理文件锁——你得自己封装。
为什么 std::fstream 不能自动加锁
打开文件只是建立 I/O 关联,操作系统级的文件锁(如 flock 或 LockFileEx)不在 C++ 标准库职责范围内。RAII 管的是资源生命周期,不是语义行为。你调用 open(),它构造成功;但没“加锁”这一步,除非你显式写。
-
std::fstream析构时会自动close(),这是 RAII 的体现 - 但
close()不会自动解锁——因为根本没锁过 - 跨进程互斥必须靠系统调用,C++ 标准不抽象这一层
怎么用 RAII 封装文件 + 锁(Linux 示例)
核心是把 int fd 和锁操作包进一个类,在构造里 open() + flock(),析构里 funlock() + close()。顺序不能反:先解锁再关 fd,否则锁可能残留。
- 构造失败时(比如文件被其他进程独占锁住),抛异常,确保不会留下半初始化对象
- 使用
fcntl(F_SETLK)而非F_SETLKW,避免阻塞——RAII 初始化不该卡住 - 注意:
flock()是建议性锁,若对方不检查,就无效;fcntl()可做强制锁,但仅限于写锁场景
class LockedFile {
int fd = -1;
public:
LockedFile(const char* path) {
fd = open(path, O_RDWR);
if (fd == -1) throw std::runtime_error("open failed");
if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
close(fd);
throw std::runtime_error("lock failed");
}
}
~LockedFile() {
if (fd != -1) {
flock(fd, LOCK_UN);
close(fd);
}
}
// 禁用拷贝,允许移动
LockedFile(const LockedFile&) = delete;
LockedFile& operator=(const LockedFile&) = delete;
};
Windows 下要注意 LockFileEx 的同步对象陷阱
Windows 没有 flock,得用 LockFileEx,但它要求句柄必须以 FILE_FLAG_OVERLAPPED 打开——而 std::fstream 底层用的 CreateFile 默认不设这个标志。所以别试图往 std::fstream 上“贴”锁逻辑。
- 直接用
HANDLE+LockFileEx封装更可靠 -
LockFileEx的锁是内核对象,进程退出自动释放,但提前UnlockFileEx更稳妥 - 不要混用
std::fstream和HANDLE操作同一文件路径——缓冲、光标位置、句柄共享都可能冲突
容易被忽略的关键点
RAII 管理的是“谁负责释放”,不是“是否安全”。文件锁的有效性高度依赖协作:所有访问方都得走同一套锁逻辑。一个脚本用 cat 直接读,你的 RAII 类再严谨也拦不住。
- 锁粒度:整个文件 vs 区域锁(
fcntl(F_SETLK)支持偏移+长度),选错会导致并发误伤 - 临时文件场景下,
O_TMPFILE(Linux)或CREATE_TEMPORARY(Windows)可避免路径竞争,但锁仍需手动加 - 异常安全只保证“析构执行”,不保证“业务逻辑正确”——比如锁住了但解析失败,要不要回滚?RAII 不管这事
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











