//创建互斥量(注意与事件Event的创建函数对比)
HANDLECreateMutex(
LPSECURITY_ATTRIBUTESlpMutexAttributes,
BOOLbInitialOwner,
LPCTSTRlpName
);
函数说明:
第一个参数表示安全控制,一般直接传入NULL。
第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。
第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。
函数访问值:
成功返回一个表示互斥量的句柄,失败返回NULL。
//打开互斥量
HANDLEOpenMutex(
DWORDdwDesiredAccess,
BOOLbInheritHandle,
LPCTSTRlpName //名称
);
函数说明:
第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示互斥量句柄继承性,一般传入TRUE即可。
第三个参数表示名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过这个函数来找到这个互斥量。
函数访问值:
成功返回一个表示互斥量的句柄,失败返回NULL。
//触发互斥量
BOOLReleaseMutex (HANDLEhMutex)
//清理互斥量
CloseHandle()
接下来我们就在经典多线程问题用互斥量来保证主线程与子线程之间的同步,由于互斥量的使用函数类似于事件Event,所以可以仿照上一篇的实现来写出代码:
#include
#include
#include
long lGlobVar = 0;//全局资源
//互斥量与关键段
HANDLE hMutexPtrParam;
CRITICAL_SECTION csGlobVar;
unsigned int WINAPI ThreadProc(void* lpParam){
//由于创建线程是要一定的开销的,所以新线程并不能第一时间执行到这来
int iThreadNo = *(int*)lpParam;
ReleaseMutex(hMutexPtrParam);//触发互斥量
Sleep(50);//Do Sth
EnterCriticalSection(&csGlobVar);
lGlobVar++;//处理全局资源
Sleep(50);//Do Sth
printf("线程编号:%d,全局变量:%d.\n", iThreadNo, lGlobVar);
LeaveCriticalSection(&csGlobVar);
return 0;
}
void main()
{
//初始化互斥量与关键段 第二个参数为TRUE表示互斥量为创建线程所有
hMutexPtrParam = CreateMutex(NULL, FALSE, NULL);
InitializeCriticalSection(&csGlobVar);
HANDLE hThreads[10];
for(int i = 0; i < 10; i++){//等子线程接收到参数时主线程可能改变了这个i的值
hThreads[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, &i, 0, NULL);//创建线程
WaitForSingleObject(hMutexPtrParam, INFINITE);//等待互斥量被触发,等待到复位后,立马置位
}
//保证子线程已全部运行结束
WaitForMultipleObjects(10, hThreads, TRUE, INFINITE);
for(int i = 0; i < 10; i++){
CloseHandle(hThreads[i]);//关闭线程句柄,使其内核对象计数减一
}
//销毁互斥量和关键段
CloseHandle(hMutexPtrParam);
DeleteCriticalSection(&csGlobVar);
}
可以看出,与关键段类似,互斥量也是不能解决线程间的同步问题。
联想到关键段会记录线程ID即有“线程拥有权”的,而互斥量也记录线程ID,莫非它也有“线程拥有权”这一说法。
答案确实如此,互斥量也是有“线程拥有权”概念的。“线程拥有权”在关键段中有详细的说明,这里就不再赘述了。另外由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用ReleaseMutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程ID设置为0,并将它的递归计数器复置为0,表示这个互斥量被触发了。然后系统将“公平地”选定一个等待线程来完成调度(被选中的线程的WaitForSingleObject()会返回WAIT_ABANDONED_0)。