使用信号量, 互斥量, 事件, 共享内存组合实现的跨进程读写锁
1.写入锁定时任何读取将会挂起
2.读取锁定时任何写入将会挂起
3.写入无锁时任何读取不会挂起
4.多个读取挂起, 多个写入挂起, 单个写入锁定时, 若解锁, 挂起的写入操作优先获得锁
CRWLockIPC.h
#pragma once
#include <windows.h>
#include <vector>
#include <string>
#include <tchar.h>
#pragma warning(disable:4200)
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
//
// @brief: 跨进程读写锁
// @copyright: Copyright 2024 FlameCyclone
// @license:
// @birth: Created by Visual Studio 2022 on 2024-12-29
// @version: V1.0.0
// @revision: last revised by FlameCyclone on 2024-12-29
//
class CRWLockIPC
{
public:
CRWLockIPC();
~CRWLockIPC();
//
// @brief: 初始化
// @param: strName 锁名, 如果无锁名, 则不可跨进程
// @param: fWritePriority 是否写操作优先
// @param: nReadCount 最大同时读取计数
// @ret: bool
bool Initialize(
const _tstring& strName = _T(""),
bool fWritePriority = true,
LONG nReadCount = LONG_MAX
);
//
// @brief: 反初始化
// @ret: void
void Uninitialize();
//
// @brief: 获取读锁
// @param: dwTimeout 超时时间(毫秒)
// @ret: bool 获取是否成功
bool AcquireReadLock(DWORD dwTimeout = INFINITE);
//
// @brief: 释放读锁
// @ret: void
void ReleaseReadLock();
//
// @brief: 获取写锁
// @param: dwTimeout 超时时间(毫秒)
// @ret: bool 获取是否成功
bool AcquireWriteLock(DWORD dwTimeout = INFINITE);
//
// @brief: 释放写锁
// @ret: void
void ReleaseWriteLock();
private:
// 共享数据结构
typedef struct _SHARE_DATA
{
LONG nReadWaitCount; // 当前读取等待计数
LONG nWriteWaitCount; // 当前写入等待计数
LONG nReadCount; // 当前读取计数
LONG nWriteCount; // 当前写入计数
}SHARE_DATA, * PSHARE_DATA;
PSHARE_DATA m_pShareData; // 共享数据(用于共享数据结构存取)
HANDLE m_hFileMapping; // 共享内存(文件映射)
HANDLE m_hReadSemaphore; // 读取信号量
HANDLE m_hWritableEvent; // 可写事件(无读取时有信号)
HANDLE m_hReadableEvent; // 可读事件(无写入时有信号)
HANDLE m_hWriteMutex; // 写入互斥量
HANDLE m_hShareMutex; // 共享数据互斥量
bool m_fInit; // 初始化结果
bool m_fWritePriority; // 写优先
};
CRWLockIPC.cpp
#include "CRWLockIPC.h"
CRWLockIPC::CRWLockIPC()
:
m_pShareData(nullptr),
m_hFileMapping(nullptr),
m_hReadSemaphore(nullptr),
m_hWritableEvent(nullptr),
m_hReadableEvent(nullptr),
m_hWriteMutex(nullptr),
m_hShareMutex(nullptr),
m_fInit(false),
m_fWritePriority(false)
{
}
CRWLockIPC::~CRWLockIPC()
{
Uninitialize();
}
bool CRWLockIPC::Initialize(
const _tstring& strName,
bool fWritePriority/* = true*/,
LONG nReadCount/* = LONG_MAX*/
)
{
SECURITY_ATTRIBUTES sa = { 0 };
SECURITY_DESCRIPTOR sd = { 0 };
bool fResult = false;
bool fCreate = false;
sa.nLength = sizeof(sa);
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = &sd;
_tstring strFileMapping = strName.empty() ? strName : strName + _T("_FileMapping");
_tstring strReadSemaphore = strName.empty() ? strName : strName + _T("_ReadSemaphore");
_tstring strShareMutex = strName.empty() ? strName : strName + _T("_ShareMutex");
_tstring strWritableEvent = strName.empty() ? strName : strName + _T("_ReadEvent");
_tstring strReadableEvent = strName.empty() ? strName : strName + _T("_WriteEvent");
_tstring strWriteMutex = strName.empty() ? strName : strName + _T("_WriteMutex");
Uninitialize();
(void)::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
(void)::SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
do
{
// 创建访问互斥量
m_hShareMutex = ::CreateMutex(
&sa,
FALSE,
strShareMutex.empty() ? NULL : strShareMutex.c_str()
);
if (!m_hShareMutex)
{
break;
}
// 创建读取信号量
m_hReadSemaphore = ::CreateSemaphore(
&sa,
nReadCount,
nReadCount,
strReadSemaphore.empty() ? NULL : strReadSemaphore.c_str()
);
if (!m_hReadSemaphore)
{
break;
}
// 创建可写手动事件
m_hWritableEvent = ::CreateEvent(
&sa,
TRUE,
TRUE,
strWritableEvent.empty() ? NULL : strWritableEvent.c_str()
);
if (!m_hWritableEvent)
{
break;
}
// 创建可读手动事件
m_hReadableEvent = ::CreateEvent(
&sa,
TRUE,
TRUE,
strReadableEvent.empty() ? NULL : strReadableEvent.c_str()
);
if (!m_hReadableEvent)
{
break;
}
// 创建写入互斥量
m_hWriteMutex = ::CreateMutex(
&sa,
FALSE,
strWriteMutex.empty() ? NULL : strWriteMutex.c_str()
);
if (!m_hWriteMutex)
{
break;
}
// 创建共享内存
m_hFileMapping = ::CreateFileMapping(
INVALID_HANDLE_VALUE,
&sa,
PAGE_READWRITE,
0,
sizeof(SHARE_DATA),
strFileMapping.empty() ? NULL : strFileMapping.c_str()
);
if (nullptr == m_hFileMapping)
{
break;
}
if (ERROR_ALREADY_EXISTS != ::GetLastError())
{
fCreate = true;
}
m_pShareData = (PSHARE_DATA)::MapViewOfFile(
m_hFileMapping,
FILE_MAP_READ | FILE_MAP_WRITE,
0,
0,
0
);
if (nullptr == m_pShareData)
{
break;
}
//首次创建, 设置信息
if (fCreate)
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
memset(m_pShareData, 0, sizeof(SHARE_DATA));
::ReleaseMutex(m_hShareMutex);
}
fResult = true;
} while (false);
if (!fResult)
{
Uninitialize();
}
m_fWritePriority = fWritePriority;
m_fInit = true;
return fResult;
}
void CRWLockIPC::Uninitialize()
{
if (m_hReadSemaphore)
{
::CloseHandle(m_hReadSemaphore);
m_hReadSemaphore = nullptr;
}
if (m_hWriteMutex)
{
::CloseHandle(m_hWriteMutex);
m_hWriteMutex = nullptr;
}
if (m_hWritableEvent)
{
::SetEvent(m_hWritableEvent);
::CloseHandle(m_hWritableEvent);
m_hWritableEvent = nullptr;
}
if (m_hReadableEvent)
{
::SetEvent(m_hReadableEvent);
::CloseHandle(m_hReadableEvent);
m_hReadableEvent = nullptr;
}
if (m_hShareMutex)
{
DWORD dwWait = ::WaitForSingleObject(m_hShareMutex, INFINITE);
if (WAIT_OBJECT_0 == dwWait)
{
if (m_pShareData)
{
::UnmapViewOfFile(m_pShareData);
m_pShareData = nullptr;
}
if (m_hFileMapping)
{
::CloseHandle(m_hFileMapping);
m_hFileMapping = nullptr;
}
}
::ReleaseMutex(m_hShareMutex);
::CloseHandle(m_hShareMutex);
m_hShareMutex = nullptr;
}
m_fInit = false;
}
bool CRWLockIPC::AcquireReadLock(DWORD dwTimeout/* = INFINITE*/)
{
bool fResult = false;
bool fAbort = false;
if (!m_fInit)
{
return false;
}
// 递增读等待计数
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
m_pShareData->nReadWaitCount++;
// 写操作优先: 若存在写操作等待,则阻止新的读操作获取锁
if (m_fWritePriority && m_pShareData->nWriteWaitCount > 0)
{
::ResetEvent(m_hReadableEvent);
}
::ReleaseMutex(m_hShareMutex);
}
// 请求读锁
do
{
// 等待可读事件有信号(无写操作时有信号)
if (WAIT_OBJECT_0 != WaitForSingleObject(m_hReadableEvent, dwTimeout))
{
fAbort = true;
break;
}
// 等待读信号量
if (WAIT_OBJECT_0 != ::WaitForSingleObject(m_hReadSemaphore, dwTimeout))
{
fAbort = true;
break;
}
} while (false);
// 递减读等待计数
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
m_pShareData->nReadWaitCount--;
::ReleaseMutex(m_hShareMutex);
}
if (!fAbort)
{
// 更新计数
::WaitForSingleObject(m_hShareMutex, INFINITE);
// 读计数递增
if (m_pShareData->nReadCount < LONG_MAX)
{
m_pShareData->nReadCount++;
}
// 设置可写事件无信号, 阻止写操作
::ResetEvent(m_hWritableEvent);
::ReleaseMutex(m_hShareMutex);
fResult = true;
} while (false);
return fResult;
}
void CRWLockIPC::ReleaseReadLock()
{
if (!m_fInit)
{
return;
}
// 释放读锁
::ReleaseSemaphore(m_hReadSemaphore, 1, NULL);
// 计数检查
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
// 更新读取计数
if (m_pShareData->nReadCount > 0)
{
m_pShareData->nReadCount--;
}
// 没有任何读操作存在则允许写操作
if (0 == m_pShareData->nReadCount)
{
// 设置可写事件有信号, 允许写操作
::SetEvent(m_hWritableEvent);
}
::ReleaseMutex(m_hShareMutex);
}
}
bool CRWLockIPC::AcquireWriteLock(DWORD dwTimeout/* = INFINITE*/)
{
bool fResult = false;
if (!m_fInit)
{
return false;
}
// 递增写入等待数
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
m_pShareData->nWriteWaitCount++;
::ReleaseMutex(m_hShareMutex);
}
// 获取写锁
do
{
// 等待可写事件
if (WAIT_OBJECT_0 != ::WaitForSingleObject(m_hWritableEvent, dwTimeout))
{
break;
}
// 等待写入互斥量事件
if (WAIT_OBJECT_0 != ::WaitForSingleObject(m_hWriteMutex, dwTimeout))
{
break;
}
fResult = true;
} while (false);
// 递减写入等待数
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
m_pShareData->nWriteWaitCount--;
::ReleaseMutex(m_hShareMutex);
}
// 获取锁成功则递增写计数, 并且重置可读事件设为无信号, 防止读线程读取
if (fResult)
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
// 更新写计数
if (m_pShareData->nWriteCount < LONG_MAX)
{
m_pShareData->nWriteCount++;
}
::ResetEvent(m_hReadableEvent);
::ReleaseMutex(m_hShareMutex);
}
return fResult;
}
void CRWLockIPC::ReleaseWriteLock()
{
if (!m_fInit)
{
return;
}
// 释放写锁
::ReleaseMutex(m_hWriteMutex);
// 递减写计数
{
::WaitForSingleObject(m_hShareMutex, INFINITE);
// 更新写计数
if (m_pShareData->nWriteCount > 0)
{
m_pShareData->nWriteCount--;
}
// 不存在写等待则设置可读事件为有信号
if (0 == m_pShareData->nWriteWaitCount)
{
::SetEvent(m_hReadableEvent);
}
::ReleaseMutex(m_hShareMutex);
}
}
main.cpp
#include <locale.h>
#include <tchar.h>
#include <time.h>
#include "Win32Utils/CStrUtils.h"
#include "Win32Utils/CRWLockIPC.h"
#include <thread>
#include <vector>
#include <mutex>
std::mutex mutex;
void CReadWriteLockTest(CRWLockIPC& rwLock, int read, int write)
{
std::vector<std::thread> vTasks;
auto cbRead = [&rwLock](int i) {
mutex.lock();
CStrUtils::ConsoleOutput(_T("read start: %d\n"), i);
mutex.unlock();
rwLock.AcquireReadLock();
mutex.lock();
CStrUtils::ConsoleOutput(_T("read data: %d\n"), i);
mutex.unlock();
rwLock.ReleaseReadLock();
};
auto cbWrite = [&rwLock](int i) {
mutex.lock();
CStrUtils::ConsoleOutput(_T("write start: %d\n"), i);
mutex.unlock();
rwLock.AcquireWriteLock();
mutex.lock();
CStrUtils::ConsoleOutput(_T("write data: %d\n"), i);
mutex.unlock();
rwLock.ReleaseWriteLock();
};
for (int i = 0; i < read; i++)
{
vTasks.emplace_back(std::thread(cbRead, i));
}
for (int i = 0; i < write; i++)
{
vTasks.emplace_back(std::thread(cbWrite, i));
}
for (auto& item : vTasks)
{
if (item.joinable())
{
item.join();
}
}
}
int ReadTest(CRWLockIPC& rwLock)
{
CHAR ch = 0;
while (true)
{
CStrUtils::ConsoleOutput(_T("按任意键获取读锁\n"));
ch = ::getchar();
CStrUtils::ConsoleOutput(_T("正在获取读锁...\n"));
bool fResult = rwLock.AcquireReadLock();
CStrUtils::ConsoleOutput(_T("已获取读锁, 按任意键释放读锁\n"));
ch = ::getchar();
rwLock.ReleaseReadLock();
if (ch == 'q')
{
return 0;
}
}
return 0;
}
int WriteTest(CRWLockIPC& rwLock)
{
CHAR ch = 0;
while (true)
{
CStrUtils::ConsoleOutput(_T("按任意键获取写锁\n"));
ch = ::getchar();
CStrUtils::ConsoleOutput(_T("正在获取写锁...\n"));
bool fResult = rwLock.AcquireWriteLock();
CStrUtils::ConsoleOutput(_T("已获取写锁, 按任意键释放写锁\n"));
ch = ::getchar();
rwLock.ReleaseWriteLock();
if (ch == 'q')
{
return 0;
}
}
return 0;
}
int _tmain(int argc, LPCTSTR argv[])
{
setlocale(LC_ALL, "");
CRWLockIPC rwLock;
rwLock.Initialize(_T("CReadWriteLock"), true);
while (true)
{
system("cls");
CReadWriteLockTest(rwLock, 32, 32);
CStrUtils::ConsoleOutput(_T("按任意键继续\n"));
CHAR ch = ::getchar();
if (ch == 'q')
{
return 0;
}
}
return 0;
}