互斥内核对象(mutex)确保线程可以互斥地访问一个资源,这也是这个内核对象这样命名的原因。对于互斥的效果,其实临界区(CRITICAL_SECTION)也实现了。它俩的区别在于互斥对象属于内核对象,而临界区属于用户模式对象。临界区只能适用于同一个进程之间的多个线程的同步,而互斥对象可以适用于不同进程间线程的同步。同时这也意味着互斥对象的运行速度比临界区的运行要慢。
互斥内核对象包含一个使用计数,一个线程ID和一个递归计数器。使用计数表示有多少个线程在使用该内核对象,线程ID是指互斥对象维护的线程的ID ,递归计数器表示当前线程调用该对象的次数。其中线程ID是互斥对象的一个重要的特征,互斥对象与其他的内核对象不同,它有一个“线程所有权”的问题,它能记住哪个线程正在拥有这个对象,并能够对此进行跟踪。
互斥对象的使用:
1>创建互斥对象:
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName );
Parameters
lpMutexAttributes
- [in] Ignored. Must be NULL.
- bInitialOwner
- [in] Boolean that specifies the initial owner of the mutex object. If this value is TRUE and the caller created the mutex, the calling thread obtains ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex. To determine if the caller created the mutex, see the Return Values section.
- lpName
- [in] Long pointer to a null-terminated string specifying the name of the mutex object. The name is limited to MAX_PATH characters and can contain any character except the backslash path-separator character (\). Name comparison is case sensitive.
-
If lpName matches the name of an existing named mutex object, the bInitialOwner parameter is ignored because it has already been set by the creation process.
If lpName is NULL, the mutex object is created without a name.
If lpName matches the name of an existing event, semaphore, or file-mapping object, the function fails and the GetLastError function returns ERROR_INVALID_HANDLE. This occurs because these objects share the same name space.
- 参数bInitialOwner 用来控制互斥对象的初始状态。如果向这个参数传递FALSE(通常情况下都传递这个值),那么系统就将互斥对象的线程ID和递归计数设置为0.这意味这该互斥对象不被任何线程拥有,因此需要发出通知信号。如果向这个参数传递TRUE,那么该对象的线程ID被设置为调用线程的ID,递归计数器被设置为1.由于线程ID是个非0数字,因此互斥对象初始时不发出通知信号。
参数lpName用于给该互斥对象命名,如果向该参数传递NULL将创建一个匿名的互斥对象,否则创建一个命名的互斥对象。 - 2>释放互斥对象
- ReleaseMutex(HANDLE hMutex)
- 3>等待互斥对象
- DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
); -
DWORD WaitForMultipleObjects( DWORD nCount, CONST HANDLE* lpHandles, BOOL fWaitAll, DWORD dwMilliseconds );
- 应用举例:1>简单的售票程序,摘自孙鑫《VC++深入详解》
- #include <windows.h>
- #include <iostream>
- using namespace std;
- DWORD WINAPI Fun1Proc(
- LPVOID lpParameter // thread data
- );
- DWORD WINAPI Fun2Proc(
- LPVOID lpParameter // thread data
- );
- int index=0;
- int tickets=100;
- HANDLE hMutex;
- void main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- hThread1=CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
- hThread2=CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- hMutex=CreateMutex(NULL,TRUE,L"tickets");
- if(hMutex)
- {
- if(ERROR_ALREADY_EXISTS==GetLastError())
- {
- cout<<"only instance can run!"<<endl;
- return;
- }
- }
- WaitForSingleObject(hMutex,INFINITE);
- ReleaseMutex(hMutex);
- ReleaseMutex(hMutex);
- Sleep(4000);
- getchar();
- }
- DWORD WINAPI Fun1Proc(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- //ReleaseMutex(hMutex);
- WaitForSingleObject(hMutex,INFINITE);
- if(tickets>0)
- {
- Sleep(1);
- cout<<"thread1 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
- ReleaseMutex(hMutex);
- }
- return 0;
- }
- DWORD WINAPI Fun2Proc(
- LPVOID lpParameter // thread data
- )
- {
- while(TRUE)
- {
- WaitForSingleObject(hMutex,INFINITE);
- if(tickets>0)
- {
- Sleep(1);
- cout<<"thread2 sell ticket : "<<tickets--<<endl;
- }
- else
- break;
- ReleaseMutex(hMutex);
- }
- return 0;
- }
2>互斥对象的另一个用途,利用调用CreateMutex创建一个命名的互斥对象时的特征:
A handle to the mutex object indicates success. If the named mutex object existed before the function call, the function returns a handle to the existing object and GetLastError returns ERROR_ALREADY_EXISTS. Otherwise, the caller created the mutex. NULL indicates failure. To get extended error information, call GetLastError.
当创建一个命名的互斥对象时,如果该互斥对象已经存在,则返回已经存在的互斥对象的句柄,此时调用GetLastErro函数将返回ERROR_ALREADY_EXISTS,利用这个特性我们可以用来保证只创建一个应用程序实例。
- m_hMutex =::CreateMutex(NULL,FALSE,STR);
- if(GetLastError()==ERROR_ALREADY_EXISTS)
- {
- AfxMessageBox(IDS_ALREADY_EXISTS);
- return FALSE;
- }