Introduction
The CReadWriteLock
class is a very simple class that implements the Reader/Writer pattern, used for synchronizing multiple thread access to a shared resource. By allowing either multiple reader access or single write access, this class provides a better throughput than standard synchronization mechanisms like Critical Sections.
Background
Looking for a simple solution on the net, I faced the fact that the solutions often were advanced, and not developed in the spirit of KISS (Keep It Simple S*****). Therefore, I decided to implement a simple yet functional solution.
Please note that in order to prevent reader starvation, the CReadWriteLock
should only be used when reader access is far more common than writer access. The reason for this is that the implementation does not contain any thread queue or similar to secure the access order, and writer threads will always win this race.
Using the code
Just copy the class (ReadWriteLock.cpp and ReadWriteLock.h) to the project folder, and add the two files to the project.
The code is pretty much self-explanatory; create an instance of CReadWriteLock
, and use respectively LockReader()
/UnlockReader()
and LockWriter()
/UnlockWriter()
before using the shared resource.
ReadWriteLock.h
##pragma once
#include "Windows.h"
class CReadWriteLock
{
public:
CReadWriteLock(void);
virtual ~CReadWriteLock(void);
void LockReader();
void UnlockReader();
void LockWriter();
void UnlockWriter();
private:
CReadWriteLock(const CReadWriteLock &cReadWriteLock);
const CReadWriteLock& operator=(const CReadWriteLock &cReadWriteLock);
void IncrementReaderCount();
void DecrementReaderCount();
HANDLE m_hWriterEvent;
HANDLE m_hNoReadersEvent;
int m_nReaderCount;
CRITICAL_SECTION m_csLockWriter;
CRITICAL_SECTION m_csReaderCount;
};
ReadWriteLock.cpp
#include "StdAfx.h"
#include "ReadWriteLock.h"
CReadWriteLock::CReadWriteLock(void)
: m_nReaderCount(0), m_hWriterEvent(NULL), m_hNoReadersEvent(NULL)
{
m_hWriterEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
m_hNoReadersEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
InitializeCriticalSection(&m_csLockWriter);
InitializeCriticalSection(&m_csReaderCount);
}
CReadWriteLock::CReadWriteLock(const CReadWriteLock &cReadWriteLock)
{
}
const CReadWriteLock& CReadWriteLock::operator=(const CReadWriteLock &cReadWriteLock)
{
return *this;
}
CReadWriteLock::~CReadWriteLock(void)
{
DeleteCriticalSection(&m_csLockWriter);
DeleteCriticalSection(&m_csReaderCount);
CloseHandle(m_hWriterEvent);
CloseHandle(m_hNoReadersEvent);
}
void CReadWriteLock::LockReader()
{
bool bLoop = true;
while(bLoop)
{
WaitForSingleObject(m_hWriterEvent, INFINITE);
IncrementReaderCount();
if(WaitForSingleObject(m_hWriterEvent, 0) != WAIT_OBJECT_0)
{
DecrementReaderCount();
}
else
{
bLoop = false;
}
}
}
void CReadWriteLock::UnlockReader()
{
DecrementReaderCount();
}
void CReadWriteLock::LockWriter()
{
EnterCriticalSection(&m_csLockWriter);
WaitForSingleObject(m_hWriterEvent, INFINITE);
ResetEvent(m_hWriterEvent);
WaitForSingleObject(m_hNoReadersEvent, INFINITE);
LeaveCriticalSection(&m_csLockWriter);
}
void CReadWriteLock::UnlockWriter()
{
SetEvent(m_hWriterEvent);
}
void CReadWriteLock::IncrementReaderCount()
{
EnterCriticalSection(&m_csReaderCount);
m_nReaderCount++;
ResetEvent(m_hNoReadersEvent);
LeaveCriticalSection(&m_csReaderCount);
}
void CReadWriteLock::DecrementReaderCount()
{
EnterCriticalSection(&m_csReaderCount);
m_nReaderCount--;
if(m_nReaderCount <= 0)
{
SetEvent(m_hNoReadersEvent);
}
LeaveCriticalSection(&m_csReaderCount);
}
History
- 2006-11-01: Version 1.0 - First release.
- 2006-11-20: Version 1.1 - Shallow copy is prevented by implementing private constructor and assignment operator.
- 2006-11-21: Version 1.2 -
while
loop waiting for no readers in LockWriter()
is replaced with WaitForSingleObject()
.