實作一個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