天天看點

讀寫鎖 share_mutex

實作一個Windows下的共享鎖(讀寫鎖)(一)

    作者:tyc611.cublog.cn,2008-11-18 在Windows Vista/Server 2008之前,Windows沒有提供共享鎖(通俗稱為讀寫鎖),隻能靠自己實作。但從Windows Vista/Server 2008開始,Windows提供了使用者态下的讀寫鎖SRWLock,效率非常高。本文實作了一個簡單的共享鎖,可用于之前的Windows系統。

實作原理:

鎖内部會記錄目前鎖類型、共享通路線程數和互斥通路線程數,并利用互斥量來保護這些内部資料,使用事件來實作共享通路線程和互斥通路線程的同步。

這裡,使用了互斥量來保護資料,效率較低(因為必須進入核心态)。但如果使用臨界區來進行保護,則無法保證離開臨界區同時等待事件觸發的原子性。有時間進一步研究是否可以使用臨界區來代替這裡的互斥量。

下面的類被命名為SharedMutex(可共享的互斥量),而不是SharedLock,原因是SharedMutex提供了共享鎖的基本功能,可以直接使用。但為了使用友善,可以在此基礎上包裝出另一個更友善的共享鎖SharedLock,這個在後續文章中給出實作。

類SharedMutex的源代碼(文章後面附有打包下載下傳):

#ifndef SHARED_MUTEX_H_INCLUDED #define SHARED_MUTEX_H_INCLUDED #ifndef _WIN32 #error "works only on Windows" #endif #include <windows.h> #include "Noncopyable.h" // 可共享的互斥量 class SharedMutex     : private Noncopyable { private:     HANDLE m_mutex;     HANDLE m_sharedEvent;     HANDLE m_exclusiveEvent;     volatile int m_sharedNum;     volatile int m_exclusiveNum;     volatile int m_lockType;     static const int LOCK_NONE = 0;     static const int LOCK_SHARED = 1;     static const int LOCK_EXCLUSIVE = 2; public:     SharedMutex();     ~SharedMutex();     // 擷取共享通路權     bool AcquireShared(DWORD waitTime = INFINITE);     // 釋放共享通路權     void ReleaseShared();     // 擷取獨占通路權     bool AcquireExclusive(DWORD waitTime = INFINITE);     // 釋放獨占通路權     void ReleaseExclusive(); }; #endif // SHARED_MUTEX_H_INCLUDED

#include "SharedMutex.h" #include <cassert> SharedMutex::SharedMutex(): m_sharedNum(0), m_exclusiveNum(0), m_lockType(LOCK_NONE) {     // 建立用于保護内部資料的互斥量     m_mutex = ::CreateMutex(NULL, FALSE, NULL);     // 建立用于同步共享通路線程的事件(手動事件)     m_sharedEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);     // 建立用于同步獨占通路線程的事件(自動事件)     m_exclusiveEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); } SharedMutex::~SharedMutex() {     ::CloseHandle(m_mutex);     ::CloseHandle(m_sharedEvent);     ::CloseHandle(m_exclusiveEvent); } // 擷取共享通路權 bool SharedMutex::AcquireShared(DWORD waitTime) {     ::WaitForSingleObject(m_mutex, INFINITE);     ++m_sharedNum;     if (m_lockType == LOCK_EXCLUSIVE) {         DWORD retCode = ::SignalObjectAndWait(m_mutex, m_sharedEvent, waitTime, FALSE);         if (retCode == WAIT_OBJECT_0) {             return true;         } else {             if (retCode == WAIT_TIMEOUT)                 ::SetLastError(WAIT_TIMEOUT);             return false;         }     }     m_lockType = LOCK_SHARED;     ::ReleaseMutex(m_mutex);     return true; } // 釋放共享通路權 void SharedMutex::ReleaseShared() {     assert(m_lockType == LOCK_SHARED);     ::WaitForSingleObject(m_mutex, INFINITE);     --m_sharedNum;     if (m_sharedNum == 0) {         if (m_exclusiveNum > 0) {             // 喚醒一個獨占通路線程             m_lockType = LOCK_EXCLUSIVE;             ::SetEvent(m_exclusiveEvent);         } else {             // 沒有等待線程             m_lockType = LOCK_NONE;         }     }     ::ReleaseMutex(m_mutex); } // 擷取獨占通路權 bool SharedMutex::AcquireExclusive(DWORD waitTime) {     ::WaitForSingleObject(m_mutex, INFINITE);     ++m_exclusiveNum;     if (m_lockType != LOCK_NONE) {         DWORD retCode = ::SignalObjectAndWait(m_mutex, m_exclusiveEvent, waitTime, FALSE);         if (retCode == WAIT_OBJECT_0) {             return true;         } else {             if (retCode == WAIT_TIMEOUT)                 ::SetLastError(WAIT_TIMEOUT);             return false;         }     }     m_lockType = LOCK_EXCLUSIVE;     ::ReleaseMutex(m_mutex);     return true; } // 釋放獨占通路權 void SharedMutex::ReleaseExclusive() {     assert(m_lockType == LOCK_EXCLUSIVE);     ::WaitForSingleObject(m_mutex, INFINITE);     --m_exclusiveNum;     // 獨占通路線程優先     if (m_exclusiveNum > 0) {         // 喚醒一個獨占通路線程         ::SetEvent(m_exclusiveEvent);     } else if (m_sharedNum > 0) {         // 喚醒目前所有共享通路線程         m_lockType = LOCK_SHARED;         ::PulseEvent(m_sharedEvent);     } else {         // 沒有等待線程         m_lockType = LOCK_NONE;     }     ::ReleaseMutex(m_mutex); }

SharedMutex的測試代碼:

#include <process.h> #include <iostream> #include "SharedMutex.h" using namespace std; SharedMutex g_mutex; const int LOOP_NUM = 2000; volatile __int64 g_data = 0; CRITICAL_SECTION g_cs; unsigned WINAPI ReaderThread(void *pParam) {     int id = (int)pParam;     ::EnterCriticalSection(&g_cs);     cout << "Reader [" << id << "] start" << endl;     ::LeaveCriticalSection(&g_cs);     __int64 max = 0;     __int64 min = 0;     for (int i = 0; i < LOOP_NUM; ++i) {         g_mutex.AcquireShared();         __int64 data = g_data;         if (data > max)             max = data;         if (data < min)             min = data;         g_mutex.ReleaseShared();         Sleep(1);     }     ::EnterCriticalSection(&g_cs);     cout << "Reader [" << id << "] quit, max = " << max << ", min = " << min << endl;     ::LeaveCriticalSection(&g_cs);     return 0; } unsigned WINAPI WriterThread1(void *pParam) {     int id = (int)pParam;     ::EnterCriticalSection(&g_cs);     cout << "Writer1 [" << id << "] start" << endl;     ::LeaveCriticalSection(&g_cs);     for (int i = 0; i < LOOP_NUM; ++i) {         g_mutex.AcquireExclusive();         g_data = g_data + i;         g_mutex.ReleaseExclusive();         Sleep(1);     }     ::EnterCriticalSection(&g_cs);     cout << "Writer1 [" << id << "] quit" << endl;     ::LeaveCriticalSection(&g_cs);     return 0; } unsigned WINAPI WriterThread2(void *pParam) {     int id = (int)pParam;     ::EnterCriticalSection(&g_cs);     cout << "Writer2 [" << id << "] start" << endl;     ::LeaveCriticalSection(&g_cs);     for (int i = 0; i < LOOP_NUM; ++i) {         g_mutex.AcquireExclusive();         g_data = g_data - i;         g_mutex.ReleaseExclusive();         Sleep(1);     }     ::EnterCriticalSection(&g_cs);     cout << "Writer2 [" << id << "] quit" << endl;     ::LeaveCriticalSection(&g_cs);     return 0; } int main() {     ::InitializeCriticalSection(&g_cs);     // 建立讀寫工作線程(建立時挂起工作線程)     HANDLE readers[20];     for (int i = 0; i < _countof(readers); ++i) {         readers[i] = (HANDLE)_beginthreadex(NULL, 0, ReaderThread, (void*)i,             CREATE_SUSPENDED, NULL);     }     HANDLE writers1[5];     for (int i = 0; i < _countof(writers1); ++i) {         writers1[i] = (HANDLE)_beginthreadex(NULL, 0, WriterThread1, (void*)i,             CREATE_SUSPENDED, NULL);     }     HANDLE writers2[5];     for (int i = 0; i < _countof(writers2); ++i) {         writers2[i] = (HANDLE)_beginthreadex(NULL, 0, WriterThread2, (void*)i,             CREATE_SUSPENDED, NULL);     }     // 恢複工作線程     for (int i = 0; i < _countof(readers); ++i) {         ResumeThread(readers[i]);     }          for (int i = 0; i < _countof(writers1); ++i) {         ResumeThread(writers1[i]);     }     for (int i = 0; i < _countof(writers2); ++i) {         ResumeThread(writers2[i]);     }     // 等待工作線程結束     WaitForMultipleObjects(_countof(readers), readers, TRUE, INFINITE);     WaitForMultipleObjects(_countof(writers1), writers1, TRUE, INFINITE);     WaitForMultipleObjects(_countof(writers2), writers2, TRUE, INFINITE);     // 釋放核心對象句柄     for (int i = 0; i < _countof(readers); ++i) {         CloseHandle(readers[i]);     }     for (int i = 0; i < _countof(writers1); ++i) {         CloseHandle(writers1[i]);     }     for (int i = 0; i < _countof(writers2); ++i) {         CloseHandle(writers2[i]);     }     ::DeleteCriticalSection(&g_cs);     cout << ">> Expected data value is " << 0 << ", and the real value is " << g_data << endl;     return 0; }

編譯運作測試:

F:\tmp\SharedLock>cl SharedMutex.cpp SharedMutex_example.cpp /EHsc /Fettt.exe /n

ologo

SharedMutex.cpp

SharedMutex_example.cpp

正在生成代碼...

F:\tmp\SharedLock>ttt.exe

Reader [0] start

Reader [1] start

Reader [2] start

Reader [3] start

Reader [4] start

Reader [5] start

Reader [6] start

Reader [7] start

Reader [8] start

Reader [9] start

Reader [10] start

Reader [11] start

Reader [12] start

Reader [13] start

Reader [14] start

Reader [15] start

Reader [16] start

Reader [17] start

Reader [18] start

Reader [19] start

Writer1 [0] start

Writer1 [1] start

Writer1 [2] start

Writer1 [3] start

Writer1 [4] start

Writer2 [0] start

Writer2 [1] start

Writer2 [2] start

Writer2 [3] start

Writer2 [4] start

Reader [3] quit, max = 8352, min = 0

Reader [2] quit, max = 8352, min = -1

Reader [1] quit, max = 9039, min = -3761

Reader [4] quit, max = 8352, min = 0

Reader [6] quit, max = 8352, min = -3453

Reader [8] quit, max = 9039, min = -1758

Reader [5] quit, max = 8352, min = -3453

Reader [0] quit, max = 8352, min = -1758

Reader [10] quit, max = 9460, min = -120

Reader [14] quit, max = 8352, min = 0

Reader [9] quit, max = 8352, min = -120

Reader [11] quit, max = 8352, min = -1758

Reader [12] quit, max = 8352, min = -1721

Reader [15] quit, max = 9460, min = 0

Reader [7] quit, max = 8352, min = -3453

Writer1 [4] quit

Reader [17] quit, max = 9460, min = -3453

Reader [13] quit, max = 8352, min = -3453

Reader [16] quit, max = 8352, min = -3453

Reader [19] quit, max = 8352, min = -120

Writer2 [4] quit

Reader [18] quit, max = 8352, min = -3453

Writer1 [1] quit

Writer2 [1] quit

Writer1 [3] quit

Writer2 [3] quit

Writer2 [0] quit

Writer1 [2] quit

Writer1 [0] quit

Writer2 [2] quit

>> Expected data value is 0, and the real value is 0

繼續閱讀