windows api中提供了一個互斥體,功能上要比臨界區強大。Mutex是互斥體的意思,當一個線程持有一個Mutex時,其它線程申請持有同一個Mutex會被阻塞,是以可以通過Mutex來保證對某一資源的互斥通路(即同一時間最多隻有一個線程通路)。
調用CreateMutex可以建立或打開一個Mutex對象,其原型如下
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
其中參數lpMutexAttributes用來設定Mutex對象的安全描述符和是否允許子程序繼承句柄。bInitialOwner表明是否将Mutex的持有者設定為調用線程。lpName參數設定Mutex的名字,該名字區分大小寫并不能包含"",最大長度為MAX_PATH,可設定為NULL表明該Mutex為匿名對象。
如果調用成功,則傳回Mutex的句柄,否則傳回NULL,如果lpName不為NULL且調用前同名的Mutex已被建立,則傳回同名Mutex的句柄,此時調用GetLastError将傳回ERROR_ALREADY_EXISTS,參數bInitialOwner将被忽略。
還可以調用OpenMutex打開建立的非匿名Mutex,原型如下
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
在成功建立或打開Mutex後,可以使用wait functions來等待并擷取Mutex的持有權。
下面的例子用來通過Mutex對象控制某一應用程式隻運作一次
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HANDLE hMutex = CreateMutex(NULL, FALSE, "Mutex_Only_One_Instance_Allowed");
if (NULL == hMutex)
{
Error("Create mutex error.");
return -1;
}
DWORD dw = WaitForSingleObject(hMutex, 0);
if (WAIT_FAILED == dw)
{
Error("Wait for mutex error.");
CloseHandle(hMutex); // 釋放句柄,當指向同一系統對象的所有句柄釋放後,該對象将被删除。
return -1;
}
else if (WAIT_TIMEOUT == dw)
{
// 另外一個執行個體正在運作
CloseHandle(hMutex);
return 1;
}
// 沒有其它執行個體在運作,本執行個體将繼續運作
// 在此實作必要的功能性代碼,如建立視窗,進入消息循環
// ……………
ReleaseMutex(hMutex); // 釋放hMutex的持有權,注意這并不等同于删除Mutex對象
CloseHandle(hMutex);
return 0;
}
其中WaitForSingleObject是等待特定對象發出信号(signaled),而Mutex對象在沒有任何線程持有時會發出信号。
與臨界區(critical section)有什麼差別,為什麼強大?它們有以下幾點不一緻:
1.critical section是局部對象,而mutex是核心對象。是以像waitforsingleobject是不可以等待臨界區的。
2.critical section是快速高效的,而mutex同其相比要慢很多
3.critical section使用範圍是單一程序中的各個線程,而mutex由于可以有一個名字,是以它是可以應用于不同的程序,當然也可以應用于同一個程序中的不同線程。
4.critical section 無法檢測到是否被某一個線程釋放,而mutex在某一個線程結束之後會産生一個abandoned的資訊。同時mutex隻能被擁有它的線程釋放。下面舉兩個應用mutex的例子,一個是程式隻能運作一個執行個體,也就是說同一個程式如果已經運作了,就不能再運作了;另一個是關于非常經典的哲學家吃飯問題的例子。
互斥體通常用于多程序之間的同步問題
程式運作單個執行個體:
#include "stdafx.h"
#include
#include
#include
using namespace std;
//當輸入s或者c時候結束程式
void PrintInfo(HANDLE& h, char t)
{
char c;
while (1)
{
cin >> c;
if (c == t)
{
ReleaseMutex(h);
CloseHandle(h);
break;
}
Sleep(100);
}
}
int main(int argc, char* argv[])
{
//建立mutex,當已經程式發現已經有這個mutex時候,就相當于openmutex
HANDLE hHandle = CreateMutex(NULL, FALSE, "mutex_test");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
cout << "you had run this program!" << endl;
cout << "input c to close this window" << endl;
PrintInfo(hHandle, ‘c’);
return 1;
}
cout << "program run!" << endl;
cout << "input s to exit program" <
PrintInfo(hHandle, ‘s’);
return 1;
}
封裝:
struct _Lock
{
_Lock(HANDLE& mtx) : _mtx(mtx), _ret(WAIT_OBJECT_0)
{
if (_mtx != 0)
_ret = WaitForSingleObject(_mtx, 100000);
}
~_Lock()
{
Release();
}
void Release()
{
if (_mtx != 0 && _ret == WAIT_OBJECT_0)
ReleaseMutex(_mtx);
}
HANDLE& _mtx;
DWORD _ret;
};
哲學家吃飯問題:
const int PHILOSOPHERS = 5; //哲學家人數
const int TIME_EATING = 50; //吃飯需要的時間 毫秒
HANDLE event[PHILOSOPHERS]; //主線程同工作線程保持同步的句柄數組
HANDLE mutex[PHILOSOPHERS]; //mutex數組,這裡相當于公共資源筷子
CRITICAL_SECTION cs; //控制列印的臨界區變量
UINT WINAPI ThreadFunc(void* arg)
{
int num = (int)arg;
DWORD ret = 0;
while (1)
{
ret = WaitForMultipleObjects(2, mutex, TRUE, 1000);
if (ret == WAIT_TIMEOUT)
{
Sleep(100);
continue;
}
EnterCriticalSection(&cs);
cout << "philosopher " << num << " eatting" << endl;
LeaveCriticalSection(&cs);
Sleep(TIME_EATING);
break;
}
//設定時間為有信号
SetEvent(event[num]);
return 1;
}
int main(int argc, char* argv[])
{
HANDLE hThread;
InitializeCriticalSection(&cs);
//循環建立線程
for (int i = 0; i < PHILOSOPHERS; i++)
{
mutex[i] = CreateMutex(NULL, FALSE, "");
event[i] = CreateEvent(NULL, TRUE, FALSE, "");
hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)i, 0, NULL);
if (hThread == 0)
{
cout << "create thread " << i << "failed with code: "
<< GetLastError() << endl;
DeleteCriticalSection(&cs);
return -1;
}
CloseHandle(hThread);
}
//等待所有的哲學家吃飯結束
DWORD ret = WaitForMultipleObjects(PHILOSOPHERS, event, TRUE, INFINITE);
if (ret == WAIT_OBJECT_0)
{
cout << "all the philosophers had a dinner!" << endl;
}
else
{
cout << "WaitForMultipleObjects failed with code: " << GetLastError() << endl;
}
DeleteCriticalSection(&cs);
for (int j = 0; j < PHILOSOPHERS; j++)
{
CloseHandle(mutex[j]);
}
return 1;
}
Further reading:
1.Windows 平台下的同步機制 (1)– 臨界區(CriticalSection)
2.Windows 平台下的同步機制 (2)– 互斥體(Mutex)
3.Windows 平台下的同步機制 (3)– 事件(Event)
4.Windows 平台下的同步機制 (4)– 信号量(Semaphore)
5.《windows核心程式設計》學習筆記(一)核心對象
Reference:
1.http://www.chinaitpower.com/A200507/2005-07-27/176735.html
2.http://www.cppblog.com/wangjt/archive/2008/01/30/42235.aspx