当前位置: 首页 > news >正文

C++读写锁以及实现方式

文章目录

      • 【C++专题】读写锁(Reader-Writer Lock)原理与实现方式(含C++11/20实践)
        • 一、读写锁核心概念
          • 1. **什么是读写锁?**
          • 2. **读写锁 vs 互斥锁**
        • 二、C++中的读写锁实现方式
      • 方案一:POSIX 读写锁(pthread_rwlock)
          • 1. **关键接口**
          • 2. **使用示例:缓存读取与更新**
          • 3. **注意事项**
      • 方案二:C++20 共享互斥锁(std::shared_mutex)
          • 1. **核心类**
          • 2. **使用示例:线程安全的计数器**
          • 3. **优势**
      • 方案三:自定义读写锁(基于互斥量与条件变量)
          • 1. **实现原理**
          • 2. **代码实现(简化版)**
          • 3. **关键点**
        • 三、读写锁的性能与陷阱
          • 1. **性能对比**
          • 2. **常见陷阱**
        • 四、最佳实践建议
        • 五、总结

【C++专题】读写锁(Reader-Writer Lock)原理与实现方式(含C++11/20实践)

一、读写锁核心概念
1. 什么是读写锁?

读写锁是一种同步机制,用于控制多个线程对共享资源的访问,其核心特性:

  • 读锁(共享锁):允许多个线程同时获取,适用于只读操作(如读取配置文件、缓存查询)。
  • 写锁(排他锁):仅允许单个线程获取,适用于写操作(如修改数据、更新缓存),此时其他线程(包括读线程)均被阻塞。

适用场景:读多写少的场景(如数据库缓存、日志系统),相比普通互斥锁(Mutex)能显著提升并发性。

2. 读写锁 vs 互斥锁
维度互斥锁(Mutex)读写锁(Reader-Writer Lock)
读操作并发同一时间仅1线程访问允许多线程同时读
写操作开销低(简单加锁)高(需协调读/写线程)
典型场景读写均衡或写多读少读多写少(如配置中心、缓存)
二、C++中的读写锁实现方式

方案一:POSIX 读写锁(pthread_rwlock)

适用范围:Linux/macOS等UNIX-like系统,C++11之前的主流方案。

1. 关键接口
#include <pthread.h>// 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);// 加读锁(阻塞式)
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 尝试加读锁(非阻塞,成功返回0,失败返回EBUSY)
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);// 加写锁(阻塞式)
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 尝试加写锁(非阻塞)
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);// 解锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);// 销毁锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
2. 使用示例:缓存读取与更新
#include <iostream>
#include <pthread.h>
#include <vector>
#include <thread>pthread_rwlock_t rwlock;
std::vector<int> cache;// 读线程:获取读锁,读取缓存
void read_cache(int id) {pthread_rwlock_rdlock(&rwlock);std::cout << "Thread " << id << " reading: ";for (auto val : cache) {std::cout << val << " ";}std::cout << std::endl;pthread_rwlock_unlock(&rwlock);
}// 写线程:获取写锁,更新缓存
void update_cache(int id, std::vector<int> new_data) {pthread_rwlock_wrlock(&rwlock);cache = new_data;std::cout << "Thread " << id << " updated cache." << std::endl;pthread_rwlock_unlock(&rwlock);
}int main() {pthread_rwlock_init(&rwlock, nullptr);cache = {1, 2, 3};// 启动3个读线程std::thread readers[3];for (int i = 0; i < 3; ++i) {readers[i] = std::thread(read_cache, i);}// 启动1个写线程(会等待读线程释放锁)std::thread writer(update_cache, 4, {4, 5, 6});// 等待所有线程结束for (auto& th : readers) th.join();writer.join();pthread_rwlock_destroy(&rwlock);return 0;
}
3. 注意事项
  • 初始化与销毁:需调用pthread_rwlock_initpthread_rwlock_destroy,避免内存泄漏。
  • 线程安全:写锁具有更高优先级,避免读线程饥饿(某些实现支持“写优先”策略)。

方案二:C++20 共享互斥锁(std::shared_mutex)

适用范围:C++20及以上标准,跨平台(Windows/Linux/macOS)。

1. 核心类
  • std::shared_mutex:底层实现读写锁语义。
  • std::shared_lock:用于获取读锁(共享锁)。
  • std::unique_lock:用于获取写锁(排他锁)。
2. 使用示例:线程安全的计数器
#include <iostream>
#include <shared_mutex>
#include <thread>
#include <vector>std::shared_mutex rw_mutex;
int counter = 0;// 读操作:统计计数器值
void read_counter(int id) {std::shared_lock<std::shared_mutex> lock(rw_mutex); // 自动获取读锁std::cout << "Thread " << id << ": Counter = " << counter << std::endl;
}// 写操作:增加计数器值
void increment_counter(int id, int times) {std::unique_lock<std::shared_mutex> lock(rw_mutex); // 自动获取写锁for (int i = 0; i < times; ++i) {counter++;}std::cout << "Thread " << id << " updated counter to " << counter << std::endl;
}int main() {std::vector<std::thread> threads;// 启动5个读线程for (int i = 0; i < 5; ++i) {threads.emplace_back(read_counter, i);}// 启动2个写线程for (int i = 5; i < 7; ++i) {threads.emplace_back(increment_counter, i, 100);}for (auto& th : threads) th.join();return 0;
}
3. 优势
  • RAII风格:通过std::shared_lockstd::unique_lock自动管理锁的生命周期,避免忘记解锁。
  • 跨平台兼容:无需依赖POSIX接口,可在Windows(如Visual Studio 2019+)和UNIX系统上编译。

方案三:自定义读写锁(基于互斥量与条件变量)

适用场景:需要理解底层原理,或在不支持C++20的环境中模拟读写锁。

1. 实现原理
  • 读计数器:记录当前获取读锁的线程数,无写锁时允许多线程读。
  • 写锁标志:表示是否有线程持有写锁,写锁获取时阻塞所有读线程。
  • 条件变量:用于写线程等待读线程释放锁,或读线程等待写锁释放。
2. 代码实现(简化版)
#include <mutex>
#include <condition_variable>class ReadWriteLock {
private:std::mutex mtx;std::condition_variable read_cv, write_cv;int reader_count = 0;bool writer_active = false;public:// 获取读锁void lock_read() {std::unique_lock<std::mutex> lock(mtx);// 等待写锁释放read_cv.wait(lock, [this]() { return !writer_active; });reader_count++;}// 释放读锁void unlock_read() {std::unique_lock<std::mutex> lock(mtx);reader_count--;if (reader_count == 0) {write_cv.notify_one(); // 唤醒等待的写线程}}// 获取写锁void lock_write() {std::unique_lock<std::mutex> lock(mtx);// 等待所有读线程释放且无写锁write_cv.wait(lock, [this]() { return reader_count == 0 && !writer_active; });writer_active = true;}// 释放写锁void unlock_write() {std::unique_lock<std::mutex> lock(mtx);writer_active = false;read_cv.notify_all(); // 唤醒所有读线程write_cv.notify_one(); // 唤醒可能等待的写线程}
};
3. 关键点
  • 读锁加锁:通过条件变量等待写锁释放,允许多线程同时持有读锁(通过reader_count计数)。
  • 写锁加锁:必须等待所有读锁释放(reader_count==0)且无其他写锁。
  • 优先级策略:此实现为“读优先”,写线程可能饥饿,可通过增加写等待标志优化为“写优先”。
三、读写锁的性能与陷阱
1. 性能对比
操作类型POSIX读写锁std::shared_mutex自定义读写锁(读优先)
读锁加锁耗时
写锁加锁耗时
适用场景系统级开发现代C++应用学习/定制化需求
2. 常见陷阱
  • 死锁:读线程持有读锁时尝试获取写锁,或写线程同时获取多个锁(需避免嵌套加锁)。
  • 饥饿:读线程过多导致写线程长期无法获取锁(可通过“写优先”策略缓解)。
  • 错误解锁:读锁释放时未正确更新计数器,或写锁释放时未通知等待线程。
四、最佳实践建议
  1. 优先使用C++20标准库std::shared_mutexstd::shared_lock提供简洁、安全的读写锁实现,推荐用于新项目。
  2. 明确锁作用域:使用RAII风格的lock_guardunique_lock,避免锁的作用域泄漏。
  3. 性能测试:在高并发场景下,通过基准测试(如Google Benchmark)比较读写锁与无锁编程(如原子操作)的性能差异。
  4. 文档与注释:在代码中注明锁的类型(读锁/写锁)和保护的共享资源,提升可维护性。
五、总结

读写锁是多线程编程中提升读性能的关键工具,其核心在于分离读/写操作的并发策略:

  • POSIX读写锁:适用于UNIX系统的高性能场景,但需手动管理锁生命周期。
  • C++20共享互斥锁:跨平台、安全、简洁,推荐现代C++项目使用。
  • 自定义实现:帮助理解底层原理,但需谨慎处理线程安全和性能问题。

合理使用读写锁能显著优化读多写少场景的并发性,但需注意避免死锁和饥饿问题,结合具体业务场景选择最优方案。

参考资料

  • C++标准文档:std::shared_mutex
  • POSIX文档:pthread_rwlock
  • 《C++ Concurrency in Action》

相关文章:

  • QtConcurrent run中抛出异常在QFutureWatcher传递原理
  • Lighttpd CGI配置:404错误排查实录
  • 对选择基于模型编程(MBD)的工作对职业发展影响的讨论 (2025)
  • 数据库安全性
  • Python训练打卡Day39
  • cursor升级至0.505,运行统计视频中的人数
  • GIS数据类型综合解析
  • NodeJS全栈开发面试题讲解——P3数据库(MySQL / MongoDB / Redis)
  • 6级翻译学习
  • 计算机视觉---YOLOv5
  • Python打卡训练营Day42
  • 深入理解短链服务:原理、设计与实现全解析
  • 鸿蒙OSUniApp结合机器学习打造智能图像分类应用:HarmonyOS实践指南#三方框架 #Uniapp
  • ERP系统中商品定价功能设计:支持渠道、会员与批发场景的灵活定价机制
  • 如何用利用deepseek的API能力来搭建属于自己的智能体-优雅草卓伊凡
  • 【无标题】安富莱V5程序移植到原子探索者F4控制板带TFT LCD显示屏
  • 进程信号简述
  • 6.01打卡
  • DDD架构
  • 【RocketMQ 生产者和消费者】- 生产者发送同步、异步、单向消息源码分析(1)
  • 邦派巴洛特网站是谁做的呀/品牌seo主要做什么
  • 网站数据接口怎么做/网站关键词排名
  • 免费下载微信小程序/绍兴百度推广优化排名
  • 二次网站开发/百度我的订单
  • 网站开发展示/网站生成
  • 网站seo完整的优化方案/郑州网络推广报价