--------------------------------------------------------------------------------
标题: 在Win32平台中,多读单写锁如何使用临界区实现?
作者: 叶飞虎
日期: 2011.11.11
--------------------------------------------------------------------------------
在多线程编程中,读写锁主要应用于写数据相对比较少,而读比较多,同时要求并发性
比较高的时候。
现给出使用临界区的读写锁代码,如下:
class TKYLockRWEx
{
public:
TKYLockRWEx();
virtual ~TKYLockRWEx();
bool LockRead();
bool LockWrite();
bool TryLockRead();
bool TryLockWrite();
void UnlockRead();
void UnlockWrite();
private:
void Lock() { EnterCriticalSection(&FRWLock); }
void Unlock() { LeaveCriticalSection(&FRWLock); }
void WaitingReader() { EnterCriticalSection(&FLockReader); }
void WaitingWriter() { EnterCriticalSection(&FLockWriter); }
inline void SetReadSignal();
inline void SetWriteSignal();
private:
CRITICAL_SECTION FRWLock;
CRITICAL_SECTION FLockReader;
CRITICAL_SECTION FLockWriter;
long FReadingCount;
long FWritingCount;
long FWaitingReadCount;
long FWaitingWriteCount;
};
// ---------------- 构造函数和析构函数 ----------------
// 构造函数
TKYLockRWEx::TKYLockRWEx()
{
// 初始化
FReadingCount = 0;
FWritingCount = 0;
FWaitingReadCount = 0;
FWaitingWriteCount= 0;
// 创建临界区和读写者
InitializeCriticalSection(&FRWLock);
InitializeCriticalSection(&FLockReader);
InitializeCriticalSection(&FLockWriter);
}
// 析构函数
TKYLockRWEx::~TKYLockRWEx()
{
// 置释放标志
Lock();
bool bWaiting = (FReadingCount > 0) || (FWritingCount == 1)
|| (FWaitingReadCount > 0)
|| (FWaitingWriteCount > 0);
FReadingCount = -1;
Unlock();
// 等待一会儿
if (bWaiting)
Sleep(10);
// 释放临界区和读写者
DeleteCriticalSection(&FLockWriter);
DeleteCriticalSection(&FLockReader);
DeleteCriticalSection(&FRWLock);
}
// ---------------- 私有方法 ----------------
// 设置读信号
inline void TKYLockRWEx::SetReadSignal()
{
// FWritingCount 作为读信号广播的个数
if (FWritingCount == 0)
FWritingCount = -FWaitingReadCount;
// 是否需要继续广播
if (FWritingCount < 0)
{
FWritingCount++;
FReadingCount++;
FWaitingReadCount--;
LeaveCriticalSection(&FLockReader);
}
else if (FWaitingWriteCount == 0) // 全部已退出锁
{
LeaveCriticalSection(&FLockReader);
LeaveCriticalSection(&FLockWriter);
}
}
// 设置写信号
inline void TKYLockRWEx::SetWriteSignal()
{
FWritingCount = 1;
FWaitingWriteCount--;
LeaveCriticalSection(&FLockWriter);
}
// ---------------- 公有方法 ----------------
// 读加锁
bool TKYLockRWEx::LockRead()
{
bool result = true;
bool bWaiting = false;
// 读数加 1
Lock();
if (FReadingCount == -1) // 释放标志
result = false;
else if ((FWritingCount == 1) || (FWaitingWriteCount > 0))
{
FWaitingReadCount++;
bWaiting = true;
}
else if (++FReadingCount == 1)
{
WaitingWriter();
WaitingReader();
}
Unlock();
// 判断是否等待读信号
if (bWaiting)
{
// 等待读信号
WaitingReader();
// 若广播个数不为零则继续置信号
Lock();
if (FWritingCount < 0)
SetReadSignal();
Unlock();
}
// 返回结果
return result;
}
// 写加锁
bool TKYLockRWEx::LockWrite()
{
bool result = true;
bool bWaiting = false;
// 写数置 1
Lock();
if (FReadingCount == -1) // 释放标志
result = false;
else if ((FWritingCount == 1) || (FReadingCount > 0))
{
FWaitingWriteCount++;
bWaiting = true;
}
else
{
FWritingCount = 1;
WaitingWriter();
WaitingReader();
}
Unlock();
// 判断是否等待写信号
if (bWaiting)
WaitingWriter();
// 返回结果
return result;
}
// 读试着加锁
bool TKYLockRWEx::TryLockRead()
{
bool result = true;
// 读数加 1
Lock();
if ((FReadingCount == -1) || (FWritingCount == 1)
|| (FWaitingWriteCount > 0))
result = false;
else if (++FReadingCount == 1)
{
WaitingWriter();
WaitingReader();
}
Unlock();
// 返回结果
return result;
}
// 写试着加锁
bool TKYLockRWEx::TryLockWrite()
{
bool result = true;
// 写数置 1
Lock();
if ((FReadingCount == -1) || (FWritingCount == 1)
|| (FReadingCount > 0))
result = false;
else
{
FWritingCount = 1;
WaitingWriter();
WaitingReader();
}
Unlock();
// 返回结果
return result;
}
// 读解锁
void TKYLockRWEx::UnlockRead()
{
Lock();
if (FReadingCount > 0)
{
// 读数减 1
FReadingCount--;
// 置读/写信号
if (FReadingCount == 0)
{
if (FWaitingWriteCount > 0)
SetWriteSignal();
else
SetReadSignal();
}
}
Unlock();
}
// 写解锁
void TKYLockRWEx::UnlockWrite()
{
Lock();
if (FWritingCount == 1)
{
// 写数置 0
FWritingCount = 0;
// 置读/写信号
if (FWaitingWriteCount > FWaitingReadCount)
SetWriteSignal();
else
SetReadSignal();
}
Unlock();
}
注:
使用临界区的读写锁代码是从使用 Event 的 TKYLockRW 类更改而来, 未经验证是否正
确, 本人只是从逻辑上确认没有bug, 倘若真的存在 bug, 欢迎拍砖指正!