下面利用信号量和互斥量来做一个读写锁类,代码如下:
//
//读写锁
功能:该类为多线程做对资源的读写锁,其中信号量用来读写互斥和写写互斥,互斥量用来读读互斥
思路概要:看代码中的注释可知
//
#include "StdAfx.h"
#include "RWLock.h"
//
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
//完成读写锁初始化
CRWLock::CRWLock()
{
InitRWLock();
}
//
CRWLock::~CRWLock()
{
DestroyRWLock();
}
BOOL CRWLock::MyWaitForSingleObject(HANDLE hObject)
{
#ifdef WIN32
DWORD result;
result = WaitForSingleObject(hObject, INFINITE);
return (result == WAIT_OBJECT_0);
#endif
}
BOOL CRWLock::InitRWLock()
{
#ifdef WIN32
m_RWLock.nReaderCount = 0;
m_RWLock.hDataLock = CreateSemaphore(NULL, 1, 1, NULL);//初始化创建信号量,在信号量的介绍中有对其参数的解释,此处不多做介绍,
//这里初始化资源个数为1,如果先做读操作请求的话,及时没有先写也会请求成功,个人觉得改为0比较好
if (m_RWLock.hDataLock == NULL)
{
return FALSE;
}
m_RWLock.hMutex = CreateMutex(NULL, FALSE, NULL);//第二个参数为false,表明互斥量不为任何线程占用,处于触发状态。
if (m_RWLock.hMutex == NULL)
{
CloseHandle(m_RWLock.hDataLock);
return FALSE;
}
return TRUE;
#else
pthread_rwlock_init(&m_RWLock, NULL);
return TRUE;
#endif
}
BOOL CRWLock::DestroyRWLock()
{
#ifdef WIN32
DWORD result = WaitForSingleObject(m_RWLock.hDataLock, INFINITE);
if (result != WAIT_OBJECT_0)
{
return FALSE;
}
CloseHandle(m_RWLock.hMutex);
CloseHandle(m_RWLock.hDataLock);
return TRUE;
#else
pthread_rwlock_destroy(&m_RWLock);
return TRUE;
#endif
}
BOOL CRWLock::AcquireReadLock()//请求读操作
{
BOOL result = TRUE;
#ifdef WIN32
if (MyWaitForSingleObject(m_RWLock.hMutex) == FALSE)//阻塞等待互斥量m_RWLock.hMutex被触发
{
return FALSE;
}
m_RWLock.nReaderCount ++;
if(m_RWLock.nReaderCount == 1)
{
result = MyWaitForSingleObject(m_RWLock.hDataLock);//当是第一个读的时候,阻塞等待信号量计数大于0,即做过写操作有数据可读,然后对信号量--,
}
/***此处做读操作***/
ReleaseMutex(m_RWLock.hMutex);//触发互斥量
return result;
#else
pthread_rwlock_rdlock(&m_RWLock);
return result;
#endif
}
BOOL CRWLock::ReleaseReadLock()
{
int result = TRUE;
#ifdef WIN32
LONG lPrevCount;
if (MyWaitForSingleObject(m_RWLock.hMutex) == FALSE)//阻塞等待互斥量m_RWLock.hMutex被触发
{
return FALSE;
}
--m_RWLock.nReaderCount;
if (m_RWLock.nReaderCount == 0)
{
result = ReleaseSemaphore(m_RWLock.hDataLock, 1, &lPrevCount);//如果为最后一个读者则信号量++,通知其他线程没有线程在做读操作,此时可以做写操作了
}
ReleaseMutex(m_RWLock.hMutex);//触发互斥量,来通知此次读操作以完成,其他线程可做读操作
return result;
#else
return pthread_rwlock_unlock(&m_RWLock);
#endif
}
BOOL CRWLock::AcquireWriteLock()
{
#ifdef WIN32
return MyWaitForSingleObject(m_RWLock.hDataLock);//可理解为等待信号量被触发,来源为ReleaseWriteLock中触发(即写操作以完成)或145行的读操作以完成
#else
return pthread_rwlock_wrlock(&m_RWLock);
#endif
}
BOOL CRWLock::ReleaseWriteLock()
{
#ifdef WIN32
int result = TRUE;
LONG lPrevCount;
result = ReleaseSemaphore(m_RWLock.hDataLock, 1, &lPrevCount);//信号量++,来通知其他线程本次写操作以完成,可以做读操作或写操作了
if (lPrevCount != 0)
{
return FALSE;
}
return result;
#else
return pthread_rwlock_unlock(&m_RWLock);
#endif
}
//
//读写锁
//
#ifndef _RWMETUX_H_
#define _RWMETUX_H_
//
#ifndef WIN32
#include
#endif
//
// 多证书链读写锁类
#ifdef WIN32
typedef struct _RWLock
{
HANDLE hMutex;
HANDLE hDataLock;
int nReaderCount;
} RWLock;
#endif
class CRWLock
{
public:
CRWLock();
~CRWLock();
BOOL AcquireReadLock(void); //请求读操作锁,请求不成功则阻塞
BOOL ReleaseReadLock(void); //释放读操作锁
BOOL AcquireWriteLock(void); //请求写操作锁,请求不成功则阻塞
BOOL ReleaseWriteLock(void); //释放写操作锁
private:
BOOL CRWLock::MyWaitForSingleObject(HANDLE hObject);
BOOL CRWLock::InitRWLock();
BOOL CRWLock::DestroyRWLock();
private:
#ifdef WIN32
RWLock m_RWLock; //读写锁
#else
pthread_rwlock_t m_RWLock;
#endif
};
#endif //_RWMETUX_H_
看了一下我转载的这些文章的作者写的秒杀多线程第十一篇 读者写者问题 这个对读写锁操作的列子,是存在问题的。第十一篇中的评论:“确实有这个问题存在。 g_hEventNoReader = CreateEvent(NULL, FALSE, TRUE, NULL); 这句使得写线程一开始就得到 WaitForSingleObject(g_hEventNoReader, INFINITE)成功执行的机会,如果在读线程对 g_nReaderCount进行修改之前让读线程Sleep(),则会出现既在读又在写的情况。”这个是存在的,导致这个的原因主要是 g_hEventWriter = CreateEvent(NULL, TRUE, TRUE, NULL); 第二个参数他设置为TRUE导致WaitForSingleObject不会把事件设置为未触发状态,而此时读线程也可以继续执行,导致读写的恶性竞争。其次第十四篇中秒杀多线程第十四篇 读者写者问题继 读写锁SRWLock 由于用到了SRWLock所以解决了这个问题,这个读写锁内部做了相应的操作吧!在此贴出这为大神的代码:
//读者与写者问题继 读写锁SRWLock
#include
#include
#include
//设置控制台输出颜色
BOOL SetConsoleColor(WORD wAttributes)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
if (hConsole == INVALID_HANDLE_VALUE)
return FALSE;
return SetConsoleTextAttribute(hConsole, wAttributes);
}
const int READER_NUM = 5; //读者个数
//关键段和事件
CRITICAL_SECTION g_cs;
SRWLOCK g_srwLock;
//读者线程输出函数(变参函数的实现)
void ReaderPrintf(char *pszFormat, ...)
{
va_list pArgList;
va_start(pArgList, pszFormat);
EnterCriticalSection(&g_cs);
vfprintf(stdout, pszFormat, pArgList);
LeaveCriticalSection(&g_cs);
va_end(pArgList);
}
//读者线程函数
unsigned int __stdcall ReaderThreadFun(PVOID pM)
{
ReaderPrintf(" 编号为%d的读者进入等待中...\n", GetCurrentThreadId());
//读者申请读取文件
AcquireSRWLockShared(&g_srwLock);
//读取文件
ReaderPrintf("编号为%d的读者开始读取文件...\n", GetCurrentThreadId());
Sleep(rand() % 100);
ReaderPrintf(" 编号为%d的读者结束读取文件\n", GetCurrentThreadId());
//读者结束读取文件
ReleaseSRWLockShared(&g_srwLock);
return 0;
}
//写者线程输出函数
void WriterPrintf(char *pszStr)
{
EnterCriticalSection(&g_cs);
SetConsoleColor(FOREGROUND_GREEN);
printf(" %s\n", pszStr);
SetConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
LeaveCriticalSection(&g_cs);
}
//写者线程函数
unsigned int __stdcall WriterThreadFun(PVOID pM)
{
WriterPrintf("写者线程进入等待中...");
//写者申请写文件
AcquireSRWLockExclusive(&g_srwLock);
//写文件
WriterPrintf(" 写者开始写文件.....");
Sleep(rand() % 100);
WriterPrintf(" 写者结束写文件");
//标记写者结束写文件
ReleaseSRWLockExclusive(&g_srwLock);
return 0;
}
int main()
{
printf(" 读者写者问题继 读写锁SRWLock\n");
printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
//初始化读写锁和关键段
InitializeCriticalSection(&g_cs);
InitializeSRWLock(&g_srwLock);
HANDLE hThread[READER_NUM + 1];
int i;
//先启动二个读者线程
for (i = 1; i <= 2; i++)
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
//启动写者线程
hThread[0] = (HANDLE)_beginthreadex(NULL, 0, WriterThreadFun, NULL, 0, NULL);
Sleep(50);
//最后启动其它读者结程
for ( ; i <= READER_NUM; i++)
hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThreadFun, NULL, 0, NULL);
WaitForMultipleObjects(READER_NUM + 1, hThread, TRUE, INFINITE);
for (i = 0; i < READER_NUM + 1; i++)
CloseHandle(hThread[i]);
//销毁关键段
DeleteCriticalSection(&g_cs);
return 0;
}