用MUTEX实现读写锁
这个需求很明确,就不罗嗦了。
第一个版本:
class ResManager {public: ResManager(HANDLE hHandle) : m_hHandle (hHandle) { WaitForSingleObject(m_hHandle, INFINITE); } ~ResManager() { ReleaseMutex(m_hHandle); }private: HANDLE m_hHandle; };class WriteMsgHelper {public: static WriteMsgHelper* getInstance() { if (m_pInstance == 0) { m_pInstance = new WriteMsgHelper(); m_hMutex = CreateMutex(0, false, 0); } return m_pInstance; } void WriteMsg(const string& str) { ResManager rm(m_hMutex); cout << str.c_str() << endl; }private: WriteMsgHelper() {} static WriteMsgHelper* m_pInstance; static HANDLE m_hMutex; };WriteMsgHelper* WriteMsgHelper::m_pInstance = 0; HANDLE WriteMsgHelper::m_hMutex = INVALID_HANDLE_VALUE;class BaseLock {public: virtual ~BaseLock() {} virtual bool getLock() = 0; virtual void releaseLock() = 0;};class WriteLock : public BaseLock {public: WriteLock(int &iReaderCount, HANDLE &hReaderMutex) : m_iReader_Count(iReaderCount), m_hMutex_Reader(hReaderMutex) { } bool getLock() { WaitForSingleObject(m_hMutex_Reader, INFINITE); if (m_iReader_Count == 0) { return true; } else { ReleaseMutex(m_hMutex_Reader); return false; } } void releaseLock() { ReleaseMutex(m_hMutex_Reader); }private: HANDLE &m_hMutex_Reader; int &m_iReader_Count; };class ReadLock : public BaseLock{public: ReadLock(int &iReaderCount, HANDLE &hReaderMutex) : m_iReaderCount(iReaderCount), m_hMutex_ReaderCount(hReaderMutex) {} bool getLock() { ResManager rm(m_hMutex_ReaderCount); m_iReaderCount++; return true; } void releaseLock() { ResManager rm(m_hMutex_ReaderCount); m_iReaderCount--; }private: int &m_iReaderCount; HANDLE &m_hMutex_ReaderCount; };struct ThreadParam { int iThreadID; BaseLock *pLock; int iRand; ThreadParam(int aiThreadID, BaseLock *apLock, int aiRand) : iThreadID(aiThreadID), pLock(apLock), iRand(aiRand) { } };const int g_iMaxSleepSeconds = 5; // thread try to writeDWORD WINAPI WriteThread(PVOID pvParam) { char buf[200] = {0}; ThreadParam *pThreadParam = (ThreadParam *)pvParam; sprintf(buf, "Thread %d try to get write lock", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); bool result = pThreadParam->pLock->getLock(); if (!result) { sprintf(buf, "Thread %d get lock failed", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); } else { sprintf(buf, "Thread %d get lock successfully", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); int iSleep = pThreadParam->iRand; sprintf(buf, "Thread %d write %d seconds", pThreadParam->iThreadID, iSleep); WriteMsgHelper::getInstance()->WriteMsg(buf); Sleep(iSleep * 1000); sprintf(buf, "Thread %d release lock", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); pThreadParam->pLock->releaseLock(); } delete(pThreadParam); return 0; }// thread try to readDWORD WINAPI ReadThread(PVOID pvParam) { char buf[200] = {0}; ThreadParam *pThreadParam = (ThreadParam*)pvParam; sprintf(buf, "Thread %d try to get read lock", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); bool result = pThreadParam->pLock->getLock(); if (!result) { sprintf(buf, "Thread %d get lock failed", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); } else { sprintf(buf, "Thread %d get lock successfully", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); int iSleep = pThreadParam->iRand; sprintf(buf, "Thread %d read %d seconds", pThreadParam->iThreadID, iSleep); WriteMsgHelper::getInstance()->WriteMsg(buf); Sleep(iSleep * 1000); sprintf(buf, "Thread %d release lock", pThreadParam->iThreadID); WriteMsgHelper::getInstance()->WriteMsg(buf); pThreadParam->pLock->releaseLock(); } delete(pThreadParam); return 0;}int _tmain(int argc, _TCHAR* argv[]){ const int iThreadNum = 5; HANDLE hReaderMutex = CreateMutex(0, false, 0); int iReaderCount = 0; WriteLock writeLock(iReaderCount, hReaderMutex); ReadLock readLock(iReaderCount, hReaderMutex); srand(time(0)); HANDLE hThreads[iThreadNum * 2] = {0}; for (int i=0; i<5; i++) { // create reader Sleep(10); ThreadParam *pThreadParam = new ThreadParam(i * 2, &readLock, rand() % g_iMaxSleepSeconds); hThreads[i*2] = CreateThread(0, 0, ReadThread, pThreadParam, 0, 0); // create writer Sleep(10); pThreadParam = new ThreadParam(i * 2 + 1, &writeLock, rand() % g_iMaxSleepSeconds); hThreads[i*2+1] = CreateThread(0, 0, WriteThread, pThreadParam, 0, 0); } WaitForMultipleObjects(iThreadNum * 2, hThreads, true, INFINITE); return 0; }
输出:
Thread 0 try to get read lockThread 0 get lock successfullyThread 0 read 1 secondsThread 1 try to get write lockThread 2 try to get read lockThread 1 get lock failedThread 2 get lock successfullyThread 3 try to get write lockThread 2 read 0 secondsThread 4 try to get read lockThread 3 get lock failedThread 2 release lockThread 5 try to get write lockThread 4 get lock successfullyThread 6 try to get read lockThread 5 get lock failedThread 7 try to get write lockThread 4 read 3 secondsThread 6 get lock successfullyThread 8 try to get read lockThread 7 get lock failedThread 9 try to get write lockThread 6 read 3 secondsThread 8 get lock successfullyThread 9 get lock failedThread 8 read 4 secondsThread 0 release lockThread 4 release lockThread 6 release lockThread 8 release lock
其中有几个类是用来做辅助作用的,可以忽略。包括ResManager (用来管理资源), WritesgHelper(用来保证消息完整的输出到标准输出上)。
大概思路是这样的,定义了一个WriteLock类和一个ReadLock类,它们继承自BaseLock类。这样的用意是线程中使用时可以不用关心锁的类型,而是调用统一的接口就可以了。WriteLock和ReadLock实际上共享了一个mutex和一个整型变量。整型变量记录了读者的数量,而该mutex即为了保护这个变量而设。读锁每次简单的增加读者的数量,释放锁时减少读者的数量。而写锁每次要检查读者的数量,只有在读者数量为0时才进行写操作,否则写操作失败。
测试结果基本反映了我们设计的意图,读者可以并发的进行读操作,而写者操作具有排他性。但是我们发现一个问题,写者经常处于饥渴状态,以至于根本拿不到锁。第二版本我们试图改掉这个问题。读写锁有两种:第一种是强读者同步,即只要写者没有进行写操作就可以进行读操作;第二种是强读者同步,即只要读者没有进行读操作就可以进行写操作。这里先想办法实现一个简单的版本,就是写者如果申请锁失败了,不返回,而是阻塞等待。但是这个实现我大概想了想,好像只用MUTEX没有好的办法实现。我要回去再好好研究一下。大侠们有好的主意吗?
第二个版本?