#pragma once
/**************************************************
写优先读写锁
读效率比 WINDOWS 的 SRWLOCK 慢一倍左右,百万级读锁要156ms(78ms)
写效率比 WINDOWS 的 SRWLOCK 慢 N 倍,百万级写锁要1516ms(62ms)
CKSRWLock 读写锁实现
CAutoLockRead 读锁的自动化(RAII 机制)
CAutoLockWrite 写锁的自动化(RAII 机制)
使用方法(代码示例):
CKSRWLock ksrwLock;
void Read()
{
CAutoLockRead lock(ksrwLock);
// TODO:读的代码
}
void Write()
{
CAutoLockWrite lock(ksrwLock);
// TODO:写的代码
}
**************************************************/
#include <cassert>
class CKSRWLock
{
public:
void LockWrite()
{
// 写等待的计数变量
assert(m_refWriteWaitCount >= 0);
InterlockedIncrement(&m_refWriteWaitCount);
// 临界区
EnterCriticalSection(&m_csWrite);
// 不可读
ResetEvent(m_eventReadEnable);
// 互斥
InterlockedExchangeAdd(&m_refCount, -m_clMAX_READ);
// 等待所有读完成
while (m_refCount < 0);
}
void UnlockWrite()
{
// 互斥
LONG init = InterlockedExchangeAdd(&m_refCount, m_clMAX_READ);
// 写等待的计数变量
LONG value = InterlockedDecrement(&m_refWriteWaitCount);
assert(value >= 0);
// 可读
SetEvent(m_eventReadEnable);
// 临界区
LeaveCriticalSection(&m_csWrite);
}
// 返回并发读的线程数
LONG LockRead()
{
// 优化,使用一个写等待的计数变量,避免直接 Wait
if (0 == m_refWriteWaitCount)
{
// 互斥
LONG iResult = InterlockedDecrement(&m_refCount);
if (iResult > 0)
{
// Lock : n -> (n - 1)
return m_clMAX_READ - iResult;
}
else
{
InterlockedIncrement(&m_refCount);
}
}
// 基本流程
while (true)
{
// 进入内核等待
if (WAIT_TIMEOUT == WaitForSingleObject(m_eventReadEnable, INFINITE))
{
return 0;
}
if (0 == m_refWriteWaitCount)
{
// 互斥
LONG iResult = InterlockedDecrement(&m_refCount);
if (iResult > 0)
{
// Lock : n -> (n - 1)
return m_clMAX_READ - iResult;
}
else
{
InterlockedIncrement(&m_refCount);
}
}
}
}
// 返回并发读的线程数(0 表示 Lock 失败)
LONG TryLockRead(DWORD dwTick = 0)
{
// 优化,使用一个写等待的计数变量,避免直接 Wait
if (0 == m_refWriteWaitCount)
{
// 互斥
LONG iResult = InterlockedDecrement(&m_refCount);
if (iResult > 0)
{
// Lock : n -> (n - 1)
return m_clMAX_READ - iResult;
}
else
{
InterlockedIncrement(&m_refCount);
}
}
if (0 == dwTick) return 0;
// 基本流程
// 进入内核等待
if (WAIT_TIMEOUT == WaitForSingleObject(m_eventReadEnable, dwTick))
{
return 0;
}
if (0 == m_refWriteWaitCount)
{
// 互斥
LONG iResult = InterlockedDecrement(&m_refCount);
if (iResult > 0)
{
// Lock : n -> (n - 1)
return m_clMAX_READ - iResult;
}
else
{
InterlockedIncrement(&m_refCount);
}
}
return 0;
}
void UnlockRead()
{
// 互斥
InterlockedIncrement(&m_refCount);
}
public:
CKSRWLock(void):m_refCount(m_clMAX_READ),m_refWriteWaitCount(0)
{
InitializeCriticalSection(&m_csWrite);
m_eventReadEnable = ::CreateEvent(NULL, TRUE, TRUE, NULL);
SetEvent(m_eventReadEnable);
}
virtual ~CKSRWLock(void)
{
try
{
DeleteCriticalSection(&m_csWrite);
CloseHandle(m_eventReadEnable);
}
catch(...)
{
}
}
protected:
// 最大不会超过 m_clMAX_READ 个并发读
static const LONG m_clMAX_READ = 1000000;
// m_refCount: using interlocked funtions to access
// 100000000 - free
// 0 - one thread is writing
// 100000000 - n - n threads are reading
volatile LONG m_refCount;
// n - n threads are waiting to wirte
volatile LONG m_refWriteWaitCount;
// once a thread to write
CRITICAL_SECTION m_csWrite;
// wait for writing complete
HANDLE m_eventReadEnable;
};
class CAutoLockWrite
{
public:
CAutoLockWrite(CKSRWLock& cs, BOOL bLock = TRUE, LPCTSTR szDebugMsg = NULL):m_cs(cs), m_bLeaved(TRUE), m_strDebugMsg(szDebugMsg)
{
if (bLock)
{
Lock();
}
}
void Lock()
{
if (m_bLeaved)
{
m_cs.LockWrite();
m_bLeaved = FALSE;
if (!m_strDebugMsg.empty())
{
// CLOG::Research(_T("WLOCK: %s Lock<<<<<<<<<<<<<<<<"), m_strDebugMsg.c_str());
}
}
}
// sometimes we need release it as soon as possible, so just call Leave().
void Unlock()
{
if (!m_bLeaved)
{
if (!m_strDebugMsg.empty())
{
// CLOG::Research(_T("WLOCK: %s UnLock>>>>>>>>>>>>>>>"), m_strDebugMsg.c_str());
}
m_cs.UnlockWrite();
m_bLeaved = TRUE;
}
}
// auto-release
virtual ~CAutoLockWrite()
{
try
{
Unlock();
}
catch(...)
{
}
}
protected:
// members
CKSRWLock& m_cs;
BOOL m_bLeaved;
tstring m_strDebugMsg;
private:
// no copy or assignment
CAutoLockWrite(const CAutoLockWrite&);
CAutoLockWrite& operator=(const CAutoLockWrite&);
};
class CAutoLockRead
{
public:
CAutoLockRead(CKSRWLock& cs, BOOL bLock = TRUE):m_cs(cs), m_bLeaved(TRUE),m_readThreadCount(0)
{
if (bLock)
{
Lock();
}
}
BOOL TryLock(DWORD dwTick = 0)
{
assert(m_bLeaved);
if (m_bLeaved)
{
m_readThreadCount = m_cs.TryLockRead(dwTick);
if (0 != m_readThreadCount)
{
m_bLeaved = FALSE;
return TRUE;
}
}
return FALSE;
}
void Lock()
{
assert(m_bLeaved);
if (m_bLeaved)
{
m_readThreadCount = m_cs.LockRead();
m_bLeaved = FALSE;
}
}
// sometimes we need release it as soon as possible, so just call Leave().
void Unlock()
{
if (!m_bLeaved)
{
m_cs.UnlockRead();
m_bLeaved = TRUE;
}
}
LONG GetReadCount()const {return m_readThreadCount;}
// auto-release
virtual ~CAutoLockRead()
{
try
{
Unlock();
}
catch(...)
{
}
}
protected:
// members
CKSRWLock& m_cs;
BOOL m_bLeaved;
LONG m_readThreadCount;
private:
// no copy or assignment
CAutoLockRead(const CAutoLockRead&);
CAutoLockRead& operator=(const CAutoLockRead&);
};#pragma once
/********************************