我需要一个读写锁,这种锁在Windows计算机上是快速的,而且通常是可移植的(包括XP,否则我只会使用与Vista一起引入的SRWLock )。我编写了这个自定义实现,部分是作为练习,部分是为了避免只为一个类包括Boost。
不过,我是个业余爱好者,像这样的并发对象是众所周知的很难测试的。我在这里张贴代码,以便其他人可以查看它,并使任何有需要的人都可以使用它。我希望如果实现有任何问题,有人会发现它。
作为记录,此实现的性能与我的Vista计算机上的本机SRWLock大致相当(但通常更差)。
#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;
};发布于 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))中断;//等待成功释放}
有许多代码块非常类似于此。我想知道是否有一个很好的方法来重构这个。我现在还没想出一个很好的,干净的重构。因为每个块中的突变是不同的,函子可能是一个选择.
受保护的:
私底下没事吧?我们期望派生的实现吗?析构函数意味着没有。
https://codereview.stackexchange.com/questions/6483
复制相似问题