首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >“快速”读写锁

“快速”读写锁
EN

Code Review用户
提问于 2011-12-02 21:47:37
回答 1查看 4.6K关注 0票数 8

我需要一个读写锁,这种锁在Windows计算机上是快速的,而且通常是可移植的(包括XP,否则我只会使用与Vista一起引入的SRWLock )。我编写了这个自定义实现,部分是作为练习,部分是为了避免只为一个类包括Boost。

不过,我是个业余爱好者,像这样的并发对象是众所周知的很难测试的。我在这里张贴代码,以便其他人可以查看它,并使任何有需要的人都可以使用它。我希望如果实现有任何问题,有人会发现它。

作为记录,此实现的性能与我的Vista计算机上的本机SRWLock大致相当(但通常更差)。

代码语言:javascript
复制
#include <windows.h>
class FastReadWriteLock
{
public:        

    FastReadWriteLock()
    {
        m_lockState = 0; // init state
        m_hReadSem = CreateSemaphore(0,0,0x10000,0); // create ananymous semaphores with maximum value much larger
        m_hWriteSem = CreateSemaphore(0,0,0x10000,0); // than any possible value of waiting count fields
    }

    ~FastReadWriteLock()
    {
        CloseHandle(m_hReadSem); // release semaphores
        CloseHandle(m_hWriteSem);
    }

    void WaitForReadLock()
    {
        while (true)
        {
            LockState lockState = m_lockState; // get local copy of lock state
            if (lockState.writeLock || lockState.writeWaiting || lockState.readLock == -1) // write lock is held/pending or read lock overflow
            {
                if (lockState.readWaiting == -1) Sleep(0); // wait overflow; force a context switch in lieu of proper waiting
                else
                {
                    LockState newState = lockState; newState.readWaiting++; // create new state with incremented wait
                    if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) // wait aquired successfully
                    {
                        WaitForSingleObject(m_hReadSem,INFINITE); // block until an unlock event occurs
                        while (true) // attempt to decrement wait count until successful
                        {
                            lockState = m_lockState; // update local copy of lock state
                            newState = lockState; newState.readWaiting--; // create new state with decremented wait
                            if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // wait released successfully
                        }
                    }
                }
            }
            else
            {                
                LockState newState = lockState; newState.readLock++; // create new state with incremented read lock count
                if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // lock aquired successfully
            }
        } // loop continues if state was modified concurrently or wait expired
    }

    bool TryReadLock()
    {
        LockState lockState = m_lockState; // get local copy of lock state
        if (lockState.writeLock || lockState.writeWaiting || lockState.readLock == -1) return false; // write lock is held/pending or read lock overflow
        LockState newState = lockState; newState.readLock++; // create new state with incremented read lock count
        return lockState == InterlockedCompareExchange(&m_lockState,newState,lockState); // attempt to aquire lock
    }

    void ReleaseReadLock()
    {
        LockState lockState = m_lockState; // get local copy of lock state
        while (lockState.readLock) // attempt to decrement read lock until successful
        {
            LockState newState = lockState; newState.readLock--; // create new state with decremented read lock count
            if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // lock released successfully
            lockState = m_lockState; // failure, reset local state copy
        } 
        if (lockState.writeWaiting)
        {
            ReleaseSemaphore(m_hWriteSem,1,NULL); // release a single waiting writer
        }
        else if (lockState.readWaiting)
        {
            ReleaseSemaphore(m_hReadSem,lockState.readWaiting,NULL); // release all waiting readers
        }
    }

    void WaitForWriteLock()
    {
        while (true)
        {
            LockState lockState = m_lockState; // get local copy of lock state
            if (lockState.writeLock || lockState.readLock) // read or write lock is currently held
            {
                if (lockState.writeWaiting == -1) Sleep(0); // wait overflow; force a context switch in lieu of proper waiting
                else
                {
                    LockState newState = lockState; newState.writeWaiting++; // create new state with incremented wait
                    if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) // wait aquired successfully
                    {
                        WaitForSingleObject(m_hWriteSem,INFINITE); // block until an unlock event occurs
                        while (true) // attempt to decrement wait count until successful
                        {
                            lockState = m_lockState; // update local copy of lock state
                            newState = lockState; newState.writeWaiting--; // create new state with decremented wait
                            if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // wait released successfully
                        }
                    }
                }
            }
            else
            {                
                LockState newState = lockState; newState.writeLock = 1; // create new state with write lock
                if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // lock aquired successfully
            }
        } // loop continues if state was modified concurrently or wait expired
    }

    bool TryWriteLock()
    {
        LockState lockState = m_lockState; // get local copy of lock state
        if (lockState.writeLock || lockState.readLock) return false; // read or write lock is currently held
        LockState newState = lockState; newState.writeLock = 1; // create new state with write lock
        return lockState == InterlockedCompareExchange(&m_lockState,newState,lockState); // attempt to aquire lock
    }

    void ReleaseWriteLock()
    {
        LockState lockState = m_lockState; // get local copy of lock state
        while (lockState.writeLock) // attempt to zero write lock until successful
        {
            LockState newState = lockState; newState.writeLock = 0; // create new state with no write lock
            if (lockState == InterlockedCompareExchange(&m_lockState,newState,lockState)) break; // lock released successfully
            lockState = m_lockState; // failure, reset local state copy
        } 
        if (lockState.writeWaiting)
        {
            ReleaseSemaphore(m_hWriteSem,1,NULL); // release a single waiting writer
        }
        else if (lockState.readWaiting)
        {
            ReleaseSemaphore(m_hReadSem,lockState.readWaiting,NULL); // release all waiting readers
        }
    }

protected:

    struct LockState
    {
        // state encoded in 32 bit word
        unsigned long   readWaiting     :11,
                        readLock        :11,                    
                        writeWaiting    :9,
                        writeLock       :1;
        // methods
        inline LockState(LONG value = 0) { *(LONG*)this = value; }
        inline operator LONG() const { return *reinterpret_cast<const unsigned long*>(this); }
        inline LockState& operator =(LONG value) { *(LONG*)this = value; return *this; }
        inline LockState& operator =(const LockState& rhs) { *(LONG*)this = (LONG)rhs; return *this; }
        inline bool operator ==(LONG value) const { return (LONG)*this == value; }
    };

    volatile LONG   m_lockState;
    HANDLE          m_hReadSem;
    HANDLE          m_hWriteSem;
};
EN

回答 1

Code Review用户

发布于 2013-11-16 22:55:56

这看起来像是作者有偏见的读取器/写入器锁。这可能很好,但如果有高比例的作家,请小心读者的饥饿。

还要注意的是,Vista+读取器/写入器锁既不是读取器,也不是作者偏倚的。我还没有找到关于它如何处理这个问题的明确文档,但根据我的经验,它倾向于在FIFO模式下运行。

~FastReadWriteLock()

复制构造函数、赋值操作符、移动构造函数和移动赋值操作符怎么办?除非需要,否则我会删除它们。

m_hReadSem = CreateSemaphore(0,0,0x10000,0);

CreateSemaphore可以返回一个错误。代码可能应该检查错误,并做一些明智的事情。Win32信号量周围的RAII包装器(即使是私有类)是我最初使用的路由。

此注释适用于正在执行的其他Win32 API调用。如果使用得当,它们就不会经常失败。但在高负载或高并发情况下,故障可能是灾难性的。

if (lockState.writeLock || lockState.writeWaiting || lockState.readLock == -1)

由于.writeLock.writeWaiting不是布尔人,而是计数器,所以考虑显式> 0测试。

ReleaseReadLock

如果两个对ReleaseReadLock的调用都运行了if (lockState.writeWaiting) ReleaseSemaphore(m_hWriteSem,1,NULL);分支,我们将唤醒两个作者。只有一个人能够获得。另一个将经历一个虚假的唤醒。这可能没问题,但我觉得我还是该指出这一点。读取器路径也可以展示这一点。

当(true) //尝试递减等待计数时,直到成功{ lockState = m_lockState;//更新锁状态newState =lockState的本地副本;newState.writeWating-;//创建具有递减等待if的新状态(lockState == InterlockedCompareExchange(&m_lockState,newState,lockState))中断;//等待成功释放}

有许多代码块非常类似于此。我想知道是否有一个很好的方法来重构这个。我现在还没想出一个很好的,干净的重构。因为每个块中的突变是不同的,函子可能是一个选择.

受保护的:

私底下没事吧?我们期望派生的实现吗?析构函数意味着没有。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/6483

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档