Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1065756
  • 博文数量: 77
  • 博客积分: 11498
  • 博客等级: 上将
  • 技术积分: 1840
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-04 11:10
文章分类

全部博文(77)

文章存档

2011年(1)

2010年(16)

2009年(5)

2008年(55)

分类: C/C++

2008-11-18 20:27:02


    实现一个Windows下的共享锁(读写锁)(一)
    作者:tyc611.cublog.cn,2008-11-18
在Windows Vista/Server 2008之前,Windows没有提供共享锁(通俗称为读写锁),只能靠自己实现。但从Windows Vista/Server 2008开始,Windows提供了用户态下的读写锁SRWLock,效率非常高。本文实现了一个简单的共享锁,可用于之前的Windows系统。

实现原理:
锁内部会记录当前锁类型、共享访问线程数和互斥访问线程数,并利用互斥量来保护这些内部数据,使用事件来实现共享访问线程和互斥访问线程的同步。

这里,使用了互斥量来保护数据,效率较低(因为必须进入内核态)。但如果使用临界区来进行保护,则无法保证离开临界区同时等待事件触发的原子性。有时间进一步研究是否可以使用临界区来代替这里的互斥量。

下面的类被命名为SharedMutex(可共享的互斥量),而不是SharedLock,原因是SharedMutex提供了共享锁的基本功能,可以直接使用。但为了使用方便,可以在此基础上包装出另一个更方便的共享锁SharedLock,这个在后续文章中给出实现。

类SharedMutex的源代码(文章后面附有打包下载):


/**
* SharedMutex.h
* @Author Tu Yongce
* @Created 2008-11-17
* @Modified 2008-11-17
* @Version 0.1
*/


#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


/**
* SharedMutex.cpp
* @Author Tu Yongce
* @Created 2008-11-17
* @Modified 2008-11-17
* @Version 0.1
*/


#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的测试代码:

/**
* SharedMutex_example.cpp
* @Author Tu Yongce
* @Created 2008-11-17
* @Modified 2008-11-17
* @Version 0.1
*/


#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

F:\tmp\SharedLock>

源代码打包下载

文件:SharedMutex.zip
大小:3KB
下载:下载


阅读(5726) | 评论(7) | 转发(0) |
给主人留下些什么吧!~~

brushington2010-11-16 23:34:47

好像对的 ::SignalObjectAndWait会释放信号量, 我要把这个算法写到OpenMP下去, 好痛苦.

brushington2010-11-16 23:02:13

代码中这样的地方是错的吧: (请看下面第10行和14行) // 获取共享访问权 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; //要在返回之前做 m_lockType = LOCK_SHARED; ::ReleaseMutex(m_mutex); } else { if (retCode == WAIT_TIMEOUT) ::