互斥鎖:互斥鎖是一個能夠處於兩態之一的變量:解鎖和加鎖。
Mutex本質上說就是一把鎖,提供對資源的獨佔訪問,因此Mutex主要的做用是用於互斥。Mutex對象的值,只有0和1兩個值。這兩個值也分別表明了Mutex的兩種狀態。值爲0, 表示鎖定狀態,當前對象被鎖定,用戶進程/線程若是試圖Lock臨界資源,則進入排隊等待;值爲1,表示空閒狀態,當前對象爲空閒,用戶進程/線程能夠Lock臨界資源,以後Mutex值減1變爲0。安全
Mutex能夠被抽象爲四個操做:
- 建立 CreateMutex(NULL, false, NULL);
- 加鎖 WaitForSingleObject(g_hMutex, INFINITE);
- 解鎖 ReleaseMutex(g_hMutex);
- 銷燬 CloseHandle(g_hMutex);多線程
WaitForSingleObject()函數解析:
WaitForSingleObject是一種Windows API函數,當等待仍在掛起狀態時,句柄被關閉,那麼函數行爲是未定義的。該句柄必須具備 SYNCHRONIZE 訪問權限。
WaitForSingleObject函數用來檢測hHandle事件的信號狀態,在某一線程中調用該函數時,線程暫時掛起,若是在掛起的dwMilliseconds毫秒內,
線程所等待的對象變爲有信號狀態,則該函數當即返回;若是超時時間已經到達dwMilliseconds毫秒,但hHandle所指向的對象尚未變成有信號狀態,函數照樣返回。函數
注意點:
1.互斥鎖與臨界區相似,都是有建立、進入、離開、銷燬等過程。可是互斥量屬於內核對象,運行較慢,而臨界區屬於用戶對象,運行速度較快。
2.建立互斥鎖的第二參數,注意:
TRUE表明主線程擁有互斥對象 可是主線程沒有釋放該對象 ,互斥對象誰擁有, 誰釋放 。
FLASE表明當前沒有線程擁有這個互斥對象。
3.
函數原型:性能
HANDLE WINAPI CreateMutex(
__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in_opt LPCTSTR lpName );spa
lpMutexAttributes : 第一個參數表示安全控制,通常直接傳入NULL。
bInitialOwner第二個參數用來肯定互斥量的初始擁有者。
若是傳入TRUE表示互斥量對象內部會記錄建立它的線程的線程ID號並將遞歸計數設置爲1,因爲該線程ID非零,因此互斥量處於未觸發狀態,表示互斥量爲建立線程擁有。
若是傳入FALSE,那麼互斥量對象內部的線程ID號將設置爲NULL,遞歸計數設置爲0,這意味互斥量不爲任何線程佔用,處於觸發狀態。
lpName第三個參數用來設置互斥量的名稱,在多個進程中的線程就是經過名稱來確保它們訪問的是同一個互斥量。線程
固有特色(優勢+缺點):
一、是一個系統核心對象,因此有安全描述指針,用完了要 CloseHandle 關閉句柄,這些是內核對象的共同特徵;
二、由於是核心對象,因此執行速度會比 Critical Sections 慢幾乎100倍的時間(固然只是相比較而言);指針
Mutex和Critical Section都是主要用於限制多線程(Multithread)對全局或共享的變量、對象或內存空間的訪問。下面是其主要的異同點(不一樣的地方用綠色表示)。code
|
Mutex對象 |
Critical Sectionblog |
性能和速度 |
慢。 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) |
源代碼: // MutexDemo.cpp : 定義控制檯應用程序的入口點。 // #include "stdafx.h" #include <Windows.h> #include <process.h> #define THREADNUM 5 HANDLE g_hMutex=NULL; int g_nCount = 0; unsigned _stdcall ThreadProc(void *lParam) { for (int i = 0; i < 1000000; ++i) { //得到互斥鎖的擁有權 WaitForSingleObject(g_hMutex, INFINITE); g_nCount++; //釋放互斥鎖的擁有權,避免死鎖 ReleaseMutex(g_hMutex); } return 0; } int _tmain(int argc, _TCHAR* argv[]) { //建立互斥鎖 //TRUE表明主線程擁有互斥對象 可是主線程沒有釋放該對象 互斥對象誰擁有 誰釋放 // FLASE表明當前沒有線程擁有這個互斥對象 g_hMutex= CreateMutex(NULL, false, NULL); if (g_hMutex == NULL) { printf("CreatrMutex Error :%d", GetLastError()); } HANDLE pThreads[THREADNUM]; //建立線程 for (int i = 0; i < THREADNUM; ++i) { pThreads[i]=(HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, 0, NULL); if (pThreads[i] == 0) { continue; i--; } } WaitForMultipleObjects(THREADNUM, pThreads, TRUE, INFINITE); printf("g_nCount=%d", g_nCount); //釋放 for (int i = 0; i < THREADNUM; ++i) { CloseHandle(pThreads[i]); } getchar(); return 0; }
三、由於是核心對象,並且能夠命名,因此能夠跨進程使用; 四、Mutex 使用正確的狀況下不會發生死鎖; 五、在「等待」一個 Mutex 的時候,能夠指定「結束等待」的時間長度; 六、能夠檢測到當前擁有互斥器全部權的線程是否已經退出!Wait……函數會返回:WAIT_ABANDONED