互斥锁:互斥锁是一个能够处于两态之一的变量:解锁和加锁。
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