はい、mutex は同じスレッド内で複数回待機できます。ドキュメントには次のように書かれています。
スレッドは、ミューテックスの所有権を取得した後、その実行をブロックすることなく、待機関数への繰り返し呼び出しで同じミューテックスを指定できます。これにより、スレッドが既に所有しているミューテックスを待機している間にスレッドがデッドロックするのを防ぎます。このような状況で所有権を解放するには、スレッドは、ミューテックスが待機関数の条件を満たしたたびに ReleaseMutex を呼び出す必要があります。
そうは言っても、待機の結果を無視しているため、コードは安全ではありません。したがって、ミューテックスによって保護されているものにアクセスする前に、実際にミューテックス ロックを所有していることを確認していません。
代わりにこれを試してください:
class CMutex
{
public:
CMutex(const TCHAR *name = NULL);
~CMutex();
bool Enter(DWORD milli = INFINITE);
void Leave();
};
CMutex::CMutex(const TCHAR *name)
: m_hMutex(CreateMutex(NULL, FALSE, name))
{
}
CMutex::~CMutex()
{
if (m_hMutex != NULL)
{
CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}
bool CMutex::Enter(DWORD milli)
{
return (WaitForSingleObject(m_hMutex, milli) == WAIT_OBJECT_0);
}
void CMutex::Leave()
{
ReleaseMutex(m_hMutex);
}
void A()
{
if (mutexObj->Enter()) // <-- CHECK THE RESULT!!!
{
B();
mutexObj->Leave();
}
}
void B()
{
if (mutexObj->Enter()) // <-- CHECK THE RESULT!!!
{
C();
mutexObj->Leave();
}
}
RAII を使用してロックを管理することで、さらに一歩進めます。
class CMutex
{
public:
CMutex(const TCHAR *name = NULL);
~CMutex();
bool Enter(DWORD milli = INFINITE);
void Leave();
class Lock
{
private:
CMutex &m_mutex;
bool m_locked;
public:
Lock(CMutex &mutex);
~Lock();
bool isLocked() const;
};
};
CMutex::CMutex(const TCHAR *name)
: m_hMutex(CreateMutex(NULL, FALSE, name))
{
}
CMutex::~CMutex()
{
if (m_hMutex != NULL)
{
CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}
bool Enter(DWORD milli)
{
return (WaitForSingleObject(m_hMutex, milli) == WAIT_OBJECT_0);
}
void Leave()
{
ReleaseMutex(m_hMutex);
}
CMutex::Lock::Lock(CMutex &mutex)
: m_mutex(mutex), m_locked(mutex.Enter())
{
}
CMutex::Lock::~Lock()
{
if (m_locked)
{
m_mutex.Leave();
m_locked = false;
}
}
CMutex::Lock::isLocked() const
{
return m_locked;
}
void A()
{
CMutex::Lock lock(*mutexObj);
if (lock.isLocked()) // <-- CHECK THE RESULT!!!
B();
}
void B()
{
CMutex::Lock lock(*mutexObj);
if (lock.isLocked()) // <-- CHECK THE RESULT!!!
C();
}
あるいは:
class CMutex
{
public:
CMutex(const TCHAR *name = NULL);
~CMutex();
bool Enter(DWORD milli = INFINITE);
void Leave();
class Lock
{
private:
CMutex &m_mutex;
public:
Lock(CMutex &mutex);
~Lock();
};
};
CMutex::CMutex(const TCHAR *name)
: m_hMutex(CreateMutex(NULL, FALSE, name))
{
if (!m_hMutex)
throw std::runtime_error("cannot create the mutex handle");
}
CMutex::~CMutex()
{
CloseHandle(m_hMutex);
m_hMutex = NULL;
}
bool Enter(DWORD milli)
{
return (WaitForSingleObject(m_hMutex, milli) == WAIT_OBJECT_0);
}
void Leave()
{
ReleaseMutex(m_hMutex);
}
CMutex::Lock::Lock(CMutex &mutex)
: m_mutex(mutex)
{
if (!m_mutex.Enter())
throw std::runtime_error("cannot lock the mutex");
}
CMutex::Lock::~Lock()
{
m_mutex.Leave();
}
void A()
{
CMutex::Lock lock(*mutexObj);
B();
}
void B()
{
CMutex::Lock lock(*mutexObj);
C();
}