互斥量(mutex)属于内核对象,它能够确保线程拥有对单个资源的互斥访问权。互斥对象包含一个使用数量,一个线程ID和一个计数器。其中,ID用于标识哪个线程拥有该互斥量,计数器用于指明线程拥有对象的次数。CreateMutex函数用于创建或打开一个命名或匿名的互斥量,描述如下:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, // 指向安全属性的指针
BOOL bInitialOwner, // 初始化互斥对象的所有者
LPCTSTR lpName // 指向互斥对象名的指针
);
参数lpMutexAttributes为指向安全属性的结构体,一般设置为null,表示默认的安全性。
参数bInitialOwer为互斥量的初始拥有者,如果为真,则创建该互斥量的线程为拥有者。反之,创建线程捕获得拥有权。
参数lpName为互斥量的名称,如果传值为null,表示为匿名对象。
另外,还有两个函数,一个是WaitForSingleObject用于主动请求共享对象的使用权,原型如下:
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数hHandle为请求对象的句柄,这里是指信号量的句柄
参数dwMilliseconds为等待时间,可以传递三种形式的参数,一个是毫秒数,一个是INFINITE,一个是0,这些代表了经过多少时间,如果没有信号则函数返回,返回值的含义具体查看MSDN。关于利用互斥量实现线程同步的例子如下:
#include <windows.h>
#include <iostream>
DWORD WINAPI Fun1Proc(LPVOID lpParameter);
DWORD WINAPI Fun2Proc(LPVOID lpParameter);
int index = 0;
int tickets = 100;
HANDLE hMutex;
int main()
{
HANDLE hThread1;
HANDLE hThread2;
//创建互斥对象
hMutex = CreateMutex(NULL, FALSE, LPCTSTR("tickets"));
if(hMutex)
{
if (ERROR_ALREADY_EXISTS == GetLastError())
{
std::cout << "only one instance can run!" << std::endl;
return 0;
}
}
//创建线程
hThread1 = CreateThread(NULL, 0, Fun1Proc, NULL, 0, NULL);
hThread2 = CreateThread(NULL, 0, Fun2Proc, NULL, 0, NULL);
CloseHandle(hThread1);
CloseHandle(hThread2);
Sleep(4000);
std::cout<< "now is the main thread in running\n";
system("pause");
return 0;
}
//线程1的入口函数
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
Sleep(1);
std::cout << "thread1 sell ticket :" << tickets-- << std::endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
//线程2的入口函数
DWORD WINAPI Fun2Proc(LPVOID lpParameter)
{
while (true)
{
WaitForSingleObject(hMutex,INFINITE);
if (tickets>0)
{
Sleep(1);
std::cout << "thread2 sell ticket :" << tickets-- << std::endl;
}
else
break;
ReleaseMutex(hMutex);
}
return 0;
}
对于一个互斥量来说,我们应做到谁拥有谁释放。如果同一线程多次拥有互斥量,则会增加互斥量的使用次数。当然要想让其他线程使用该互斥量,那么首先就要释放同等的次数,这样当互斥量变为有信号状态,其他线程就可以使用该互斥量了。