std::shared_mutex不适合写优先场景因其默认读优先且不可配置:只要存在读线程,新读请求立即通过,写线程持续排队可能被饿死;标准库未提供写优先开关或内部控制接口,无法改造其实现写优先语义。

为什么标准库的 std::shared_mutex 不适合写优先
它默认是读优先(read-preferring):只要有一个读线程持有锁,后续读请求能立刻进入,写线程就得一直排队。写线程可能被大量短时读操作持续饿死——尤其在高读低写、但写操作又不能延迟的场景下(比如实时配置更新、状态快照刷盘),这不可接受。
标准库没提供写优先开关,也没暴露内部计数器或唤醒策略,没法“改造”它实现写优先语义。
用 std::mutex + std::condition_variable 手写写优先锁的关键逻辑
核心是两个计数器 + 一个写等待队列控制权:用 readers 记当前活跃读线程数,writers_waiting 记已阻塞等待的写线程数;关键判断是——只要 writers_waiting > 0,就拒绝新读线程入场,哪怕当前没写线程在执行。
-
读锁获取:先锁
mtx_,检查writers_waiting == 0且writing_ == false,才允许递增readers并返回;否则cv_read_.wait() -
写锁获取:锁
mtx_后立即writers_waiting++,然后循环等待readers == 0 && writing_ == false,满足后置writing_ = true -
读锁释放:递减
readers,若readers == 0 && writers_waiting > 0,调用cv_write_.notify_one()唤醒一个写线程 -
写锁释放:置
writing_ = false,然后cv_read_.notify_all()(放行所有等待读线程)或cv_write_.notify_one()(如果还想保持严格 FIFO 写队列)
容易被忽略的唤醒竞争和虚假唤醒问题
条件变量 wait() 必须配合 while 循环检查谓词,不能用 if ——否则可能因虚假唤醒或调度顺序导致读线程在 writers_waiting > 0 时错误进入。
立即学习“C++免费学习笔记(深入)”;
另一个坑是 notify 的粒度:cv_read_.notify_all() 看似合理,但会唤醒所有等待读线程,而此时可能仍有写线程在队列里;更稳妥的做法是只在 writers_waiting == 0 时才 notify_all,否则只 notify_one 给写线程,避免读线程抢在写之前重新占满资源。
示例片段(简化):
void lock_shared() {
std::unique_lock<std::mutex> lk(mtx_);
cv_read_.wait(lk, [this] { return writers_waiting_ == 0 && !writing_; });
++readers_;
}
性能与可移植性取舍点
手写锁比 std::shared_mutex 多一次用户态条件变量唤醒路径,吞吐略低,但换来了确定性的写优先行为。Linux 下可用 pthread_rwlockattr_setkind_np() 设置写优先属性,但这是 GNU 扩展,Windows 和 macOS 不支持,跨平台项目必须回避。
真正难处理的是“写线程中途退出未解锁”的情况——没有 RAII 封装的话极易死锁。务必用 std::unique_lock 配合作用域自动管理,别裸调 lock()/unlock()。
写优先不是银弹:当写操作本身很重(如 memcpy 几十 MB),又频繁触发,读线程会长时间饥饿。这时得结合业务做限流或降级,锁机制只解决同步逻辑,不解决负载失衡。


















