Mutex和Critical Section都是主要用於限制多線程(Multithread)對全局或共享的變量、對象或內存空間的訪問。下面是其主要的異同點(不同的地方用綠色表示)。
|
Mutex |
Critical Section |
性能和速度 |
慢。 Mutex 是內核對象,相關函數的執行 (WaitForSingleObject, ReleaseMutex)需要用戶模式(User Mode)到內核模式 (Kernel Mode)的轉換,在x86處理器上這種轉化一般要 發費600個左右的 CPU指令週期。 |
快。 Critical Section本身不是內核對象,相關函數 (EnterCriticalSection,LeaveCriticalSection) 的調用一般都在用戶模式內執行,在x86處理器上 一般只需要發費9個左右的 CPU指令週期。只有 當想要獲得的鎖正好被別的線程擁有時纔會退化 成和Mutex一樣,即轉換到內核模式,發費600個 左右的 CPU指令週期。 |
能否跨越進程(Process)邊界 |
可以 |
不可 |
定義寫法 |
HANDLE hmtx; |
CRITICAL_SECTION cs; |
初始化寫法 |
hmtx= CreateMutex (NULL, FALSE, NULL); |
InitializeCriticalSection(&cs); |
結束清除寫法 |
CloseHandle(hmtx); |
DeleteCriticalSection(&cs); |
無限期等待的寫法 |
WaitForSingleObject (hmtx, INFINITE); |
EnterCriticalSection(&cs); |
0等待(狀態檢測)的寫法 |
WaitForSingleObject (hmtx, 0); |
TryEnterCriticalSection(&cs); |
任意時間等待的寫法 |
WaitForSingleObject (hmtx, dwMilliseconds); |
不支持 |
鎖釋放的寫法 |
ReleaseMutex(hmtx); |
LeaveCriticalSection(&cs); |
能否被一道用於等待其他內核對象 |
可以(使用WaitForMultipleObjects, WaitForMultipleObjectsEx, MsgWaitForMultipleObjects, MsgWaitForMultipleObjectsEx等等) |
不可 |
當擁有鎖的線程死亡時 |
Mutex變成abandoned狀態,其他的等待線程可以獲得鎖。 |
Critical Section的狀態不可知(undefined), 以後的動作就不能保證了。 |
自己會不會鎖住自己 |
不會(對已獲得的Mutex,重複調用WaitForSingleObject不會 鎖住自己。但最後你別忘了要調用同樣次數的 ReleaseMutex) |
不會(對已獲得的Critical Section,重複調用 EnterCriticalSection不會鎖住自己。但最後 你別忘了要調用同樣次數的 LeaveCriticalSection) |
下面是一些補充:
l 請先檢查你的設計,把不必要的全局或共享對象改爲局部對象。全局的東西越少,出問題的可能就越小。
l 每次你使用EnterCriticalSection時,請不要忘了在函數的所有可能返回的地方都加上LeaveCriticalSection。對於Mutex也同樣。若你把這個問題和Win32 structured exception或C++ exception一起考慮,你會發現問題並不是那麼簡單。自定義一個封裝類可能是一種解決方案,以Critical Section爲例的代碼如下所示:
class csholder
{
CRITICAL_SECTION *cs;
public:
csholder(CRITICAL_SECTION *c): cs(c)
{ EnterCriticalSection(cs); }
~csholder() { LeaveCriticalSection(cs); }
};
CRITICAL_SECTION some_cs;
void foo()
{
// ...
csholder hold_some(&some_cs);
// ... CS protected code here
// at return or if an exception happens
// hold_some's destructor is automatically called
}
l 根據你的互斥範圍需求的不同,把Mutex或Critical Section定義爲類的成員變量,或者靜態類變量。
l 若你想限制訪問的全局變量只有一個而且類型比較簡單(比如是LONG或PVOID型),你也可以使用InterlockedXXX系列函數來保證一個線程寫多個線程讀。
Demo程序
CRITICAL_SECTION g_cs;
{
cout << index++ << endl;
}
{
cout << index++ << endl;
}
void changeMe3()
{
cout << index++ << endl;
}
{
while(1)
{
Sleep(1600); //sleep 1.6 s
// 進入臨界區
EnterCriticalSection(&g_cs);
//WaitForSingleObject(hMutex, INFINITE);
//cout << "a" << index++ << endl;
changeMe();
changeMe2();
changeMe3();
//ReleaseMutex(hMutex);
LeaveCriticalSection(&g_cs);
}
return 0;
}
{
while(1)
{
Sleep(2000); //sleep 2 s
// 進入臨界區
EnterCriticalSection(&g_cs);
// 等待互斥對象通知
//WaitForSingleObject(hMutex, INFINITE);
changeMe();
changeMe2();
changeMe3();
//ReleaseMutex(hMutex);
// 離開臨界區
LeaveCriticalSection(&g_cs);
}
return 0;
}
{
// 創建互斥對象
//hMutex = CreateMutex(NULL, TRUE, NULL);
// 初始化臨界區
InitializeCriticalSection(&g_cs);
HANDLE hThread2;
hThread1 = CreateThread(NULL, 0, th1, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, th2, NULL, 0, NULL);
int k;
cin >> k;
printf("Hello World!\n");
return 0;
}