共享互斥锁(SharedMutex)详解
什么是读写锁?
读写锁是一种特殊的锁,允许多个读者同时读取,但写者必须独占。
普通互斥锁: ┌─────────────────────────────────┐ │ 同一时间只能有一个线程(读或写) │ └─────────────────────────────────┘ 读写锁: ┌─────────────────────────────────┐ │ 读者1 读者2 读者3 ← 可以同时 │ │ │ │ 写者1 ← 必须独占(没有其他读/写)│ └─────────────────────────────────┘为什么需要读写锁?
场景:配置文件 - 读取配置:非常频繁(每秒1000次) - 修改配置:很少(每天几次) 用普通mutex:所有操作串行,性能差 用读写锁:读操作并发,性能好状态图
┌─────────────────────────────────────────────┐ │ 空闲状态 │ │ readers_=0, writer_=false │ └──────────────┬──────────────┬───────────────┘ │ │ lock_shared() lock() │ │ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ 读取状态 │ │ 写入状态 │ │ readers_>0 │ │ writer_=true │ │ writer_=false │ │ readers_=0 │ │ │ │ │ │ 允许更多读者加入 │ │ 其他所有人等待 │ └──────────────────┘ └──────────────────┘带注释的代码
#include<condition_variable>#include<mutex>classSharedMutex{private:std::mutex mtx_;// Internal mutex to protect statestd::condition_variable cv_;// For threads to wait and wake upintreaders_=0;// Count of active readersboolwriter_=false;// Is there an active writer?public:// ============================================// Acquire shared (read) lock// Multiple threads can hold this simultaneously// ============================================voidlock_shared(){std::unique_lock<std::mutex>lock(mtx_);// Wait until no writer is active// Readers can coexist with other readers// But must wait if a writer is workingcv_.wait(lock,[this](){return!writer_;});// Now safe to read, increment reader count++readers_;// Note: lock releases here, other readers can enter}// ============================================// Release shared (read) lock// ============================================voidunlock_shared(){std::unique_lock<std::mutex>lock(mtx_);// One less reader--readers_;// If I'm the last reader, wake up waiting writersif(readers_==0){cv_.notify_all();}}// ============================================// Acquire exclusive (write) lock// Only one thread can hold this// No readers allowed during write// ============================================voidlock(){std::unique_lock<std::mutex>lock(mtx_);// Wait until:// 1. No other writer is active// 2. No readers are readingcv_.wait(lock,[this](){return!writer_&&readers_==0;});// Now I'm the exclusive writerwriter_=true;}// ============================================// Release exclusive (write) lock// ============================================voidunlock(){std::unique_lock<std::mutex>lock(mtx_);// Done writingwriter_=false;// Wake up everyone (readers and writers)// They will compete for the lockcv_.notify_all();}};// ============================================// RAII wrapper for shared lock// Automatically locks on construction// Automatically unlocks on destruction// ============================================template<typenameMutex>classSharedLock{private:Mutex&mtx_;public:// Constructor: acquire shared lockexplicitSharedLock(Mutex&mtx):mtx_(mtx){mtx_.lock_shared();}// Destructor: release shared lock// Called automatically when object goes out of scope~SharedLock(){mtx_.unlock_shared();}// Non-copyable (prevent double unlock)SharedLock(constSharedLock&)=delete;SharedLock&operator=(constSharedLock&)=delete;};// ============================================// RAII wrapper for exclusive lock// ============================================template<typenameMutex>classUniqueLock{private:Mutex&mtx_;public:// Constructor: acquire exclusive lockexplicitUniqueLock(Mutex&mtx):mtx_(mtx){mtx_.lock();}// Destructor: release exclusive lock~UniqueLock(){mtx_.unlock();}// Non-copyableUniqueLock(constUniqueLock&)=delete;UniqueLock&operator=(constUniqueLock&)=delete;};使用示例
#include<iostream>#include<thread>#include<vector>SharedMutex rw_mutex;intshared_data=0;// 读者线程voidreader(intid){SharedLock<SharedMutex>lock(rw_mutex);// 自动获取读锁std::cout<<"Reader "<<id<<" reads: "<<shared_data<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));// 自动释放读锁(析构时)}// 写者线程voidwriter(intid){UniqueLock<SharedMutex>lock(rw_mutex);// 自动获取写锁++shared_data;std::cout<<"Writer "<<id<<" writes: "<<shared_data<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));// 自动释放写锁(析构时)}intmain(){std::vector<std::thread>threads;// 启动多个读者和写者for(inti=0;i<3;i++){threads.emplace_back(reader,i);}threads.emplace_back(writer,0);for(inti=3;i<6;i++){threads.emplace_back(reader,i);}threads.emplace_back(writer,1);for(auto&t:threads){t.join();}return0;}执行时序图
时间 ──────────────────────────────────────────► Reader1: ████████ Reader2: ████████ (多个读者同时读) Reader3: ████████ Writer0: ████████ (写者独占) Reader4: ████████ Reader5: ████████ (又可以多个读者同时) Reader6: ████████ Writer1: ████████ (写者独占)案例:
#include<chrono>#include<iostream>#include<thread>#include<vector>#include"my_shared.h"classDataStore{private:intdata_=0;mutableSharedMutex mtx_;public:intread()const{SharedLock<SharedMutex>lock(mtx_);std::cout<<"[Thread "<<std::this_thread::get_id()<<"] Reading: "<<data_<<"\n";std::this_thread::sleep_for(std::chrono::milliseconds(100));returndata_;}voidwrite(intvalue){UniqueLock<SharedMutex>lock(mtx_);std::cout<<"[Thread "<<std::this_thread::get_id()<<"] Writing: "<<value<<"\n";data_=value;std::this_thread::sleep_for(std::chrono::milliseconds(100));}};intmain(){DataStore store;std::vector<std::thread>threads;// 3 readersfor(inti=0;i<3;++i){threads.emplace_back([&store](){for(intj=0;j<3;++j){store.read();}});}// 1 writerthreads.emplace_back([&store](){for(intj=1;j<=3;++j){store.write(j*100);}});for(auto&t:threads){t.join();}return0;}总结
| 概念 | 说明 |
|---|---|
readers_ | 当前正在读的线程数 |
writer_ | 是否有线程正在写 |
lock_shared() | 获取读锁,等待没有写者 |
lock() | 获取写锁,等待没有读者和写者 |
| RAII | 构造时加锁,析构时解锁,防止忘记解锁 |
写者优先的读写锁(Writer Priority SharedMutex)详解
为什么需要写者优先?
普通读写锁的问题: 时间 ─────────────────────────────────────────────► Reader1: ████████ Reader2: ████████ Reader3: ████████ Reader4: ████████ Reader5: ████████ ... Writer: 等待...等待...等待...等待... (饿死了!) 读者不断进来,写者永远拿不到锁 = 写者饥饿写者优先: 时间 ─────────────────────────────────────────────► Reader1: ████████ Reader2: ████████ ↓ 写者来了,新读者必须等待 Writer: ████████ ↓ 写者完成,读者才能继续 Reader3: ████████ Reader4: ████████状态图
┌─────────────────────────────────────────────────────┐ │ 空闲状态 │ │ readers_=0, writer_=false, waiting_writers_=0 │ └───────────────┬─────────────────────┬───────────────┘ │ │ lock_shared() lock() │ │ ▼ ▼ ┌────────────────────┐ ┌────────────────────────────┐ │ 读取状态 │ │ 写入状态 │ │ readers_ > 0 │ │ writer_ = true │ │ writer_ = false │ │ readers_ = 0 │ │ │ │ │ │ 如果 waiting_writers_>0 │ │ │ │ 新读者不能进入! │ │ │ └────────────────────┘ └────────────────────────────┘关键区别
普通读写锁: ┌─────────────────────────────────────────┐ │ 读者等待条件: !writer_ │ │ → 只要没人写,读者就进来 │ └─────────────────────────────────────────┘ 写者优先读写锁: ┌─────────────────────────────────────────┐ │ 读者等待条件: !writer_ && waiting_writers_==0 │ │ → 没人写 AND 没人等着写,读者才能进来 │ └─────────────────────────────────────────┘带注释的代码
#include<condition_variable>#include<mutex>classSharedMutexWriterPriority{private:std::mutex mtx_;std::condition_variable reader_cv_;// Readers wait on thisstd::condition_variable writer_cv_;// Writers wait on thisintreaders_=0;// Count of active readersintwaiting_writers_=0;// Count of writers waiting in queueboolwriter_=false;// Is there an active writer?public:// ============================================// Acquire shared (read) lock// Writers have priority over new readers!// ============================================voidlock_shared(){std::unique_lock<std::mutex>lock(mtx_);// Wait if:// 1. A writer is currently writing, OR// 2. Writers are waiting (give them priority!)//// This is the KEY difference from basic SharedMutex// New readers must wait for all waiting writersreader_cv_.wait(lock,[this](){return!writer_&&waiting_writers_==0;});// Safe to read now++readers_;}// ============================================// Release shared (read) lock// ============================================voidunlock_shared(){std::unique_lock<std::mutex>lock(mtx_);--readers_;// If I'm the last reader, wake up ONE waiting writer// (not all, just one - more efficient)if(readers_==0){writer_cv_.notify_one();}}// ============================================// Acquire exclusive (write) lock// ============================================voidlock(){std::unique_lock<std::mutex>lock(mtx_);// Register myself as waiting writer// This blocks new readers from entering!++waiting_writers_;// Wait until no writer AND no readerswriter_cv_.wait(lock,[this](){return!writer_&&readers_==0;});// Got the lock, no longer waiting--waiting_writers_;writer_=true;}// ============================================// Release exclusive (write) lock// ============================================voidunlock(){std::unique_lock<std::mutex>lock(mtx_);writer_=false;// Priority: wake writers firstif(waiting_writers_>0){// More writers waiting, let them go firstwriter_cv_.notify_one();}else{// No writers waiting, let all readers goreader_cv_.notify_all();}}};两种读写锁对比
普通读写锁 写者优先读写锁 ────────── ────────────── condition_variable 1个(共用) 2个(分开) 读者等待条件 !writer_ !writer_ && waiting_writers_==0 写者等待条件 !writer_ && !writer_ && readers_==0 readers_==0 unlock时 notify_all 先notify写者 没写者再notify读者 优点 简单公平 写者不会饿死 缺点 写者可能饿死 读者可能饿死执行时序对比
场景: R1, R2进入 → W1请求 → R3, R4请求 → W1完成 → W2请求 普通读写锁: ──────────────────────────────────────────────────────────► R1: ████████████████████ R2: ████████████████████ R3: ████████████████████████ ← R3进入(不管W1在等) R4: ████████████████████ ← R4进入(不管W1在等) W1: ████████ ← 等很久! W2: ████████ 写者优先: ──────────────────────────────────────────────────────────► R1: ████████████ R2: ████████████ ↓ W1开始等待,阻止新读者 W1: ████████ W2: ████████ ← W2优先 R3: ████████ ← 写者都完成后 R4: ████████使用示例
#include<iostream>#include<thread>#include<vector>#include<chrono>SharedMutexWriterPriority rw_mutex;intshared_data=0;voidreader(intid){std::cout<<"Reader "<<id<<" waiting..."<<std::endl;rw_mutex.lock_shared();std::cout<<"Reader "<<id<<" reading: "<<shared_data<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));rw_mutex.unlock_shared();std::cout<<"Reader "<<id<<" done"<<std::endl;}voidwriter(intid){std::cout<<"Writer "<<id<<" waiting..."<<std::endl;rw_mutex.lock();++shared_data;std::cout<<"Writer "<<id<<" writing: "<<shared_data<<std::endl;std::this_thread::sleep_for(std::chrono::milliseconds(100));rw_mutex.unlock();std::cout<<"Writer "<<id<<" done"<<std::endl;}intmain(){std::vector<std::thread>threads;// 先启动读者threads.emplace_back(reader,1);threads.emplace_back(reader,2);std::this_thread::sleep_for(std::chrono::milliseconds(10));// 写者请求threads.emplace_back(writer,1);std::this_thread::sleep_for(std::chrono::milliseconds(10));// 更多读者请求 (会被阻塞,因为写者在等待)threads.emplace_back(reader,3);threads.emplace_back(reader,4);for(auto&t:threads){t.join();}return0;}总结
| 概念 | 说明 |
|---|---|
waiting_writers_ | 等待中的写者数量,用于阻止新读者 |
reader_cv_ | 读者专用的条件变量 |
writer_cv_ | 写者专用的条件变量 |
| 核心思想 | 写者开始等待时,新读者就不能进入 |
| 适用场景 | 写操作重要、不能延迟的场景 |