Win32/C++ 跨进程读写锁

使用信号量, 互斥量,  事件, 共享内存组合实现的跨进程读写锁

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值