经常想写一点小代码给自己用。在封装的时候,一些资源例如 std::stringstream 总想多次重复使用。但是在局部代码区域的静态变量在多线程环境下
存在不明显的安全问题。所以实现了一个简单的资源锁。实现共享读,独占写的简单功能。带有测试demo
直接上代码:
ReadWriteLock.h:#pragma once
#ifndef _MFC_VER
#include <Windows.h>
#endif
class ReadWriteLock
{
public:
enum LockLevel
{
LockLevelNone,
LockLevelRead,
LockLevelWrite
};
public:
ReadWriteLock(void);
~ReadWriteLock(void);
BOOL Lock(LockLevel level = LockLevelRead, int iTimeOutMs = INFINITE);
BOOL Unlock();
private:
HANDLE m_hWriteEvent;
HANDLE m_hReadEvent;
LockLevel m_currentLevel;
CRITICAL_SECTION m_csStateChange;
int m_iReadingCount;
};
ReadWriteLock.cpp
#include "ReadWriteLock.h"
#include <assert.h>
ReadWriteLock::ReadWriteLock( void )
{
//在写数据和读数据的时候,m_hWriteEvent无信号
m_hWriteEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
//在读数据的时候, m_hReadEvent和m_hWriteEvent都无信号
m_hReadEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
m_currentLevel = LockLevelNone;
m_iReadingCount = 0;
InitializeCriticalSection(&m_csStateChange);
}
ReadWriteLock::~ReadWriteLock(void)
{
DeleteCriticalSection(&m_csStateChange);
if (m_hWriteEvent)
{
CloseHandle(m_hWriteEvent);
}
if (m_hReadEvent)
{
CloseHandle(m_hReadEvent);
}
}
BOOL ReadWriteLock::Lock( LockLevel level /*= LockLevelRead*/, int iTimeOutMs /*= INFINITE*/ )
{
assert(level!= LockLevelNone);
BOOL bResult = FALSE;
DWORD dwWaitResult = 0;
if (level == LockLevelRead)
{
//要读,保证没人在写
if (m_currentLevel != LockLevelRead)
{
dwWaitResult = WaitForSingleObject(m_hWriteEvent, iTimeOutMs);
if (dwWaitResult != WAIT_OBJECT_0)
{
bResult = FALSE;
}
else
{
EnterCriticalSection(&m_csStateChange);
//设置读信号为无信号状态
ResetEvent(m_hReadEvent);
//这是候写信号也是没信号的了
//我们虽然不用写,但是也不能让他有信号
m_currentLevel = level;
m_iReadingCount++;
bResult = TRUE;
LeaveCriticalSection(&m_csStateChange);
}
}
else
{
EnterCriticalSection(&m_csStateChange);
//设置读信号为无信号状态
ResetEvent(m_hReadEvent);
//我们虽然不用写,但是也不能让他有信号
ResetEvent(m_hWriteEvent);
m_currentLevel = level;
m_iReadingCount++;
bResult = TRUE;
LeaveCriticalSection(&m_csStateChange);
}
}
else if (level == LockLevelWrite)
{
//要写,保证没人写和没人读
dwWaitResult = WaitForSingleObject(m_hWriteEvent, iTimeOutMs);
if (dwWaitResult != WAIT_OBJECT_0)
{
bResult = FALSE;
}
else
{
//没人写但是有人读
dwWaitResult = WaitForSingleObject(m_hReadEvent, iTimeOutMs);
if (dwWaitResult != WAIT_OBJECT_0)
{
bResult = FALSE;
}
else
{
//没人写,没人读,设置写信号为无信号状态
//此时,读,写,都已经是无信号,不需要手工设置
EnterCriticalSection(&m_csStateChange);
bResult = FALSE;
m_currentLevel = level;
bResult = TRUE;
LeaveCriticalSection(&m_csStateChange);
}
}
}
else
{
assert("Invalid LockLevel" == NULL);
}
return bResult;
}
BOOL ReadWriteLock::Unlock()
{
//这种写法,保证函数只有一个出口
EnterCriticalSection(&m_csStateChange);
BOOL bResult = FALSE;
DWORD dwWaitResult = 0;
//没锁住鼓捣啥啊
assert(m_currentLevel != LockLevelNone);
if (m_currentLevel == LockLevelRead)
{
//设置读写信号为有信号
SetEvent(m_hReadEvent);
m_iReadingCount--;
if (m_iReadingCount == 0)
{
m_currentLevel = LockLevelNone;
//保证没人读才给写
SetEvent(m_hWriteEvent);
}
}
else if (m_currentLevel == LockLevelWrite)
{
//设置读写信号为有信号
SetEvent(m_hReadEvent);
SetEvent(m_hWriteEvent);
m_currentLevel = LockLevelNone;
}
else
{
assert("Invalid LockLevel" == NULL);
}
LeaveCriticalSection(&m_csStateChange);
return bResult;
}
测试的main.cpp:
#include <iostream>
#include "ReadWriteLock.h"
using namespace std;
DWORD WINAPI ReadThreadProc1(LPVOID lpParam)
{
ReadWriteLock& lock = *(ReadWriteLock*)lpParam;
for (int i = 0; i < 10; i++)
{
cout << "第1次读" << endl;
lock.Lock(ReadWriteLock::LockLevelRead);
cout << "第1次读..." << endl;
Sleep(800);
lock.Unlock();
cout << "第1次读完" << endl;
}
return 0;
}
DWORD WINAPI ReadThreadProc2(LPVOID lpParam)
{
ReadWriteLock& lock = *(ReadWriteLock*)lpParam;
for (int i = 0; i < 10; i++)
{
cout << "第2次读" << endl;
lock.Lock(ReadWriteLock::LockLevelRead);
cout << "第2次读..." << endl;
Sleep(1000);
lock.Unlock();
cout << "第2次读完" << endl;
}
return 0;
}
DWORD WINAPI WriteThreadProc1(LPVOID lpParam)
{
ReadWriteLock& lock = *(ReadWriteLock*)lpParam;
for (int i = 0; i < 10; i++)
{
cout << "第1次写" << endl;
lock.Lock(ReadWriteLock::LockLevelWrite);
cout << "第1次写..." << endl;
Sleep(2000);
lock.Unlock();
cout << "第1次写完" << endl;
}
return 0;
}
DWORD WINAPI WriteThreadProc2(LPVOID lpParam)
{
ReadWriteLock& lock = *(ReadWriteLock*)lpParam;
for (int i = 0; i < 10; i++)
{
cout << "第2次写" << endl;
lock.Lock(ReadWriteLock::LockLevelWrite);
cout << "第2次写..." << endl;
Sleep(1000);
lock.Unlock();
cout << "第2次写完" << endl;
}
return 0;
}
int main()
{
ReadWriteLock lock;
HANDLE hThread = NULL;
hThread = CreateThread(NULL, 0, ReadThreadProc1, &lock, NULL, NULL);
CloseHandle(hThread);
hThread = CreateThread(NULL, 0, ReadThreadProc2, &lock, NULL, NULL);
CloseHandle(hThread);
hThread = CreateThread(NULL, 0, WriteThreadProc1, &lock, NULL, NULL);
CloseHandle(hThread);
hThread = CreateThread(NULL, 0, WriteThreadProc2, &lock, NULL, NULL);
CloseHandle(hThread);
cin.ignore(2);
return 0;
}