首页 > 编程知识 正文

C++ 读写锁,读写锁的实现过程

时间:2023-05-05 13:34:34 阅读:57401 作者:1135

在多线程中,必须锁定对关键资源的访问,并且可以选择类似排他锁的粗粒度锁或基于CAS指令的原子语句来实现排他访问。 有关CAS等原子操作的概念,请参照以下文章。

无锁定队列实现

读/写锁定的工作原理:如果线程中没有“写锁定”,则多个线程可以同时保持读锁定,从而大大提高了对共享资源的访问效率。 “读取锁定”是用于读取共享资源的场景,因此多个线程同时保持读取锁定不会破坏共享资源的数据。 但是,如果“写锁定”保持在线程上,则阻止了读线程获取读锁定的操作,也阻止了其他写线程获取写锁定的操作,因此写锁定是排他锁定,如排他锁定和旋转锁定因为读锁是共享锁,读锁可以同时由多个线程维护。

了解读写锁定的工作原理后,请访问读写锁在读多写少的场景,能发挥出优势

此外,根据实现的不同,读写锁定可以分为“读取优先锁定”和“写入优先锁定”。

以下代码利用atomic实现了轻量的读写资源锁定。 与利用排他锁定实现的读写锁定相比,本锁定更像自旋锁定。 通过CPU提供的CAS函数(Compare And Swap ),在“用户状态”下完成锁定和解锁操作,不主动发生线程上下文切换,因此比排他锁定快、开销小,且

lock.readLock (; lock.readLock (; lock.readUnlock (; lock.readUnlock (; 也允许在写入状态下嵌套读取。 例如

lock.writeLock (; lock.writeLock (; lock.readLock (; lock.readUnlock (; lock.writeUnlock (; lock.writeUnlock (; 实现文件如下。 # include atomic # include thread # includecassertclassrwmutex { enum { write _ lock _ status=-1,frite _ status } public ~rwmutex(=default; rwmutex(constrwmutex )=delete; rwmutexoperator=(constrwmutex )=delete; int ReadLock (; int ReadUnlock (; int WriteLock (; int write unlock (private : staticconststd :3360 thread :3360 id null _ thead; //最初为0的线程id const bool WRITE_FIRST; //写入优先标志STD :3360 thread 33603360 IDM _ write threadid; //当前写入线程STD :3360 atomic _ intm _ lock count { free _ status }; //1为写入状态,0为自由状态,0为共享读取状态STD :3360 atomic _ uintm _ writewaitcount {0}; //等待写入线程计数器STD :3360 atomic _ uintm _ readwaitcount {0}; //等待写入线程计数器; const STD :3360 thread :3360 idrwmutex 33603360 null _ thead; rwmutex :3360 rwmutex (boolwritefirst/*=false */) :write_first(writefirst ),m_writeThreadId ) { } } m_readWaitCount; int count=0; if(write_first ) do(/如果写入优先级、等待写入线程数大于0,则为while(count=m_lockcount.load ) STD :内存_ ordery

::this_thread::yield(); } } while (!m_lockCount.compare_exchange_weak(count,count + 1,std::memory_order_release)); else do { while ((count = m_lockCount.load(std::memory_order_acquire)) == WRITE_LOCK_STATUS) { std::this_thread::yield(); } } while (!m_lockCount.compare_exchange_weak(count, count + 1, std::memory_order_release)); --m_readWaitCount; return m_lockCount.load(std::memory_order_acquire);}int RWMutex::ReadUnlock(){ // 若为写独占状态, 不需要解锁 if (std::this_thread::get_id() != m_writeThreadId) { return --m_lockCount; } return m_lockCount.load(std::memory_order_acquire);}int RWMutex::WriteUnlock(){ if (std::this_thread::get_id() != m_writeThreadId) { throw std::runtime_error("writeLock/Unlock mismatch"); } assert(WRITE_LOCK_STATUS == m_lockCount); m_writeThreadId = NULL_THEAD; m_lockCount.store(FREE_STATUS, std::memory_order_release); return m_lockCount.load(std::memory_order_acquire);}int RWMutex::WriteLock(){ // 若为写独占状态, 不需要加锁 if (std::this_thread::get_id() == m_writeThreadId) { return m_lockCount; } ++m_writeWaitCount; for (int iZero = FREE_STATUS; !m_lockCount.compare_exchange_weak(iZero, WRITE_LOCK_STATUS, std::memory_order_release); iZero = FREE_STATUS) { // 无论读/写 优先,都要让出资源 std::this_thread::yield(); while (!WRITE_FIRST && m_readWaitCount > 0) { // 在读优先的情况下,则等待读线程为空 std::this_thread::yield(); } } --m_writeWaitCount; m_writeThreadId = std::this_thread::get_id(); return m_lockCount;}// RAIItemplate <typename ReadMutex>class ReadLockRAII{public: explicit ReadLockRAII(ReadMutex &lockable) :m_ReadLockable(lockable) { m_ReadLockable.ReadLock(); } ~ReadLockRAII() { m_ReadLockable.ReadUnlock(); }private: ReadMutex& m_ReadLockable;};template <typename WriteMutex>class WriteLockRAII{public: explicit WriteLockRAII(WriteMutex &lockable) :m_WriteLockable(lockable) { m_WriteLockable.WriteLock(); } ~WriteLockRAII() { m_WriteLockable.WriteUnlock(); }private: WriteMutex& m_WriteLockable;};// 读锁typedef ReadLockRAII<RWMutex> CReadLock;// 写锁typedef WriteLockRAII<RWMutex> CWriteLock; 使用方法如下: #include <iostream> // std::cout/endl#include "RWMutex.h"int i = 0;RWMutex g_rwMutex;void func1(){ int iCnt = 100; do { CReadLock readLock(g_rwMutex); ++i; std::cout << "func1: " << std::this_thread::get_id() <<" "<< i << std::endl << std::flush; } while (--iCnt>0); }void func2(){ int iCnt = 100; do { CWriteLock readLock(g_rwMutex); ++i; std::cout << "func2: " << std::this_thread::get_id() << " " << i << std::endl << std::flush; } while (--iCnt > 0);}int main(){ std::thread thread1(func1); thread1.detach(); std::thread thread2(func1); thread2.detach(); std::thread thread3(func2); thread3.detach(); std::thread thread4(func2); thread4.detach(); getchar(); return 0;} 反思:

读优先锁对于读线程并发性更好,但也不是没有问题。我们试想一下,如果一直有读线程获取读锁,那么写线程将永远获取不到写锁,这就造成了写线程「饥饿」的现象。

写优先锁可以保证写线程不会饿死,但是如果一直有写线程获取写锁,读线程也会被「饿死」。

既然不管优先读锁还是写锁,对方可能会出现饿死问题,那么我们就不偏袒任何一方,搞个「公平读写锁」。

公平读写锁比较简单的一种方式是:用队列把获取锁的线程排队,不管是写线程还是读线程都按照先进先出的原则加锁即可,这样读线程仍然可以并发,也不会出现「饥饿」的现象。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。