分类: C/C++
2008-11-18 20:27:02
/**
* 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_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;
}
|
cd_yangling2015-05-02 22:01:53
下面的代码可以证明 最新的MSDN https://msdn.microsoft.com/zh-cn/library/windows/desktop/ms686293%28v=vs.85%29.aspx remark提及的SOAW函数与PulseEvent可能的死锁
static DWORD WINAPI consumer(LPVOID args)
{
for(;;) {
counter++;
WaitForSingleObject(hMutex, INFINITE);
waiters++;
SignalObjectAndWait(hMutex, hEvent, INFINITE, FALSE);
}
}
static DWORD WINAPI producer(LPVOID args)
cd_yangling2015-05-02 22:00:55
从win7开始 SignalObjectAndWait 已经不再确保 是原子的动作,当然这对于自动事件没影响,但是对于PulseEvent和SignalObjectAndWait 影响很大。会导致信号丢失。
要想在win7 .2008 正确运行你的读写锁程序,必须把 读锁 (共享锁) 的HANDLE 更换成 信号量 .PulseEvent 采取 ReleaseSemaphore(rd_event, rd_counter, NULL);
代替。释放rd_counter个信号量值。初始化的时候,使用CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); 信号量对于 SignalObjectAndWait的非原子操作不敏感,所以
可以移植到win7和2008下,为了兼容,其实一直都应该避免使用PulseEvent来唤醒多个线程,因为Semapore 的DOWN 操作也可以达到一样的目的。
chinaunix网友2011-06-01 15:25:08
AcquireShared() return false 时理论上是不应该调用ReleaseShared(),所以AcquireShared() return false 前要 --m_sharedNum; AcquireExclusive也有相同问题。
chinaunix网友2011-01-07 19:55:13
~~知道了。因为在 if (retCode == WAIT_OBJECT_0) 是在if (m_lockType != LOCK_NONE) 的条件下(也就是m_lockType = LOCK_SHARED条件),在ReleaseShared()中已经m_lockType = LOCK_EXCLUSIVE; ::SetEvent(m_exclusiveEvent); 了。。。。
chinaunix网友2011-01-06 21:41:31
我也有同样的以为。虽然SignalObjectAndWait()会ReleaseMutex(),但是 if (retCode == WAIT_OBJECT_0) { return true; //要在返回之前做 m_lockType = LOCK_SHARED; } else 。。。。 在返回之前 m_lockType = LOCK_SHARED; 应该设置一下吧?? 望解答呀