互斥量内核对象确保线程对某一个单一资源拥有一个互斥排外的访问权限.互斥量内核对象和临界区域有点相似,不过一个处于用户态(速度快),一个处于内核态(速度慢).由于互斥量是内核对象,所以不同进程的线程也可以访问同一个互斥量;而临界区域就不行,只有同一个进程内的线程才能访问得到。
一个互斥量内核对象包括:一个使用计数(usage count), 一个线程ID(thread ID),一个递归计数(recursion counter)。线程ID标记当前拥有互斥量的线程;递归计数标记这个线程拥有互斥量的次数;
下面再介绍互斥量相关函数
No.1 互斥量内核对象创建函数——CreateMutex函数
HANDLEWINAPI CreateMutex(
__in_opt LPSECURITY_ATTRIBUTESlpMutexAttributes,
__in BOOLbInitialOwner,
__in_opt LPCTSTRlpName );
具体的函数说明查看上面的MSDN链接,
第一个参数lpMutexAttributes指定安全属性,如果其值为NULL,则这个句柄不能被子进程继承。
第二个参数bInitialOwner,如果是TRUE,则表示当前创建互斥量的线程拥有该互斥量;否则就没有拥有。
第三个参数是指定互斥量名的,可以为NULL。
No.2 互斥量内核对象创建函数——CreateMutexEx函数
HANDLEWINAPI CreateMutexEx(
__in_opt LPSECURITY_ATTRIBUTESlpMutexAttributes,
__in_opt LPCTSTRlpName,
__in DWORDdwFlags,
__in DWORDdwDesiredAccess );
这个函数与CreateMutex函数相似,不过它可以在dwDesiredAccess参数中指明访问权限掩码。具体说明就请查看MSDN链接。
No.3 打开一个创建好的互斥量——OpenMutex函数
HANDLEWINAPI OpenMutex(
__in DWORD dwDesiredAccess,
__in BOOLbInheritHandle,
__in LPCTSTRlpName);
第一个参数deDesiredAccess就不再解释了;
第二个参数bInheritHandle指明当前进程的子进程是否可以继承该互斥量句柄;
第三个参数lpName指定要打开的互斥量名。
No.4 释放占用的互斥量——ReleaseMutex函数
BOOLReleaseMutex( HANDLE hMutex );
一个线程通过调用一个等待函数(WaitForSingleObject或WaitForMultiObjects)来获取对某个共享资源的访问权限,等待函数的参数就是保护资源的互斥量句柄。
在等待函数内部,首先检测互斥量的线程ID是否为0。
如果线程ID为0,就把线程ID稍微当前调用的线程ID号,将递归计数设为1,当前调用线程处于可调度状态。
如果线程ID不为0,就表示已有线程占用该互斥量,则当前的调用线程进入等待状态;只有当互斥量的线程ID重新被置为0时,就执行上面“如果线程ID为0”的相应操作。
一当某个线程占用某个互斥量时,就可以确保互斥访问了,只要线程不调用ReleaseMutex函数释放互斥量,其他线程就都会被拒绝访问该互斥量保护的资源。
一个没有占用互斥量的线程调用ReleaseMutex函数会发生什么呢?这时候ReleaseMutex会检测当前线程的ID和Mutex内部记录的线程ID是否一致,不一致则直接返回FALSE。
如果占用互斥量(Mutex)的线程,在没有调用ReleaseMutex函数就退出了(比如说调用了ExitThread, TerminateThread, ExitProcess, or TerminateProcess函数),这时候系统该怎么办呢?不过不采取措施,那么这个互斥量(Mutex)保护的资源将一直不能被访问。由于系统记录的所有线程内核对象和互斥量,通过对比就可以检测出哪个线程在没有调用ReleaseMutex函数释放互斥量的情况下就退出了,进而系统自动释放该互斥量(Mutex)将其线程ID和递归计数设置为0。
一个示例:
HANDLE g_hMutex; void WINAPI MutexCallback() { WaitForSingleObject(g_hMutex, INFINITE); gv_value = 0; ReleaseMutex(g_hMutex); } void test() { // Prepare the mutex g_hMutex = CreateMutex(NULL, false, NULL); CreateThread(NULL, 0, MutexCallback, NULL, 0, NULL); CloseHandle(g_hMutex); }
示例说明:
step1:调用CreateMutex函数创建Mutex,由于第二个参数为false,所以初始化时没有线程占用该Mutex;
step2:在线程对应的MutexCallback函数里调用WaitForSingleObject试图去占用该Mutex,
如果Mutex被线程占用,则MutexCallBack函数执行阻塞,直到Mutex释放后被其占用;
如果Mutex无线程占用,则MutexCallBack函数对应的线程立即占用,WaitForSingleObject函数返回后继续执行。
step3:MutexCallBack函数对应的线程继续执行,当完成操作就需要调用ReleaseMutex去释放Mutex,让其他需要的线程去占用。