互斥器的功能是,使多个线程和谐工作。同一时间内,只能有一个线程得到互斥对象,并获得资源操作权限,那么如果同一时间其他线程也想去操作资源,此时就会因为Mutex未处于激发状态,而无奈的等待…这时候,线程就会进入blocking(阻塞)状态,直到Mutex让出来。
总结下Mutex的操作步骤,分为以下几个功能:
1. 产生一个全局互斥器Mutex(一个Mutex可以看做一个资源,如果要多个资源,则需要创建多个Mutex句柄);
2. 锁住互斥器Mutex:获得一个Mutex的拥有权,其他线程只能等待。当需要锁操作时,如果此时锁未处于激活状态,线程就得等待(也就是阻塞状态/sleep)并每隔一段时间尝试着再次去占用Mutex,不行就继续阻塞直到Mutex被让出;
3. 释放互斥器Mutex,使得后一个等待的线程能够拥有它并得以获得资源;
这里需要说明的是,Mutex的拥有权并非属于那个产生它的线程,而是最后那个获得它且未释放的线程。线程拥有了Mutex就好像线程进入了临界区域一样。一次只能有一个线程获得Mutex。
和大部分核心对象一样,Mutex是通过计数来实现互斥,当Mutex被占用时计数为1,当Mutex未被占用时计数降低为0;
但是,有些情况是残酷的所以必须避免:在一个程序中,线程绝对不应该在即将结束时还拥有一个Mutex,否则该Mutex将无法被释放。
Win32下Mutex相关接口:
1. 创建一个互斥器,该接口返回一个HANDLE:
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName);
2. 打开一个互斥器,该互斥器已被创建过:
HANDLE OpenMutexW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName);
3. 等待一个资源对象:
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);
--hHandle:mutex句柄
--dwMilliseconds:INFINITE:表示无限等待,否则可以设置时间(ms)
4. 等待多个资源对象:
DWORD WaitForMultipleObjects(DWORD nCount, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds);
--nCount:句柄的数量
--*lpHandles:句柄数组
--bWaitAll:如果为TRUE则所有对象都空闲则获得权限,如果为FALSE则只要有一个对象空闲就获得权限
--dwMilliseconds:INFINITE:表示无限等待,否则可以设置时间(ms)
5. 等待多个资源对象,还可以等待消息:
DWORD MsgWaitForMultipleObjects(DWORD nCount, LPHANDLE pHandles, BOOL fWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask);
6. 释放互斥器
BOOL ReleaseMutex(HANDLE hMutex);
7. 摧毁互斥器
BOOL CloseHandle(HANDLE hObject);
哲学家就餐:
我们知道,哲学家就餐问题就是为了解决所有老头都拿着一支筷子,但都在等待另外双筷子进行吃饭的尴尬场面。
比如一共5个哲学家,但也只有5支筷子。如果大家同时抓起一根筷子,很快就进入死锁场面;但如果不允许老头子拿起一根筷子,而是只允许一次拿起一双筷子,此时就可以保证至少2个老头能够先吃饭了,即使还留着一根筷子另外3个老头也只能傻等——这样就能避免大家都吃不到饭的问题
那么如何使用Mutex来对哲学家问题进行解决呢?