简述
互斥锁是用在多线程间对操作同一资源进行互斥的。一个线程占用了一个资源,那么别的线程就操作此资源,直到这个线程该释放互斥锁,其他的线程才开始可以重新抢夺这个互斥锁,成功获得互斥锁的线程利用这个资源,其他线程再次阻塞,周而复始。如对全局变量的访问,线程加锁后对变量进行读写操作,完成后释放互斥锁。比如多个线程对一个全局变量进行累加并打印。
相关API
函数原型
HANDLE WINAPI CreateMutexA(
_In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes,
_In_ BOOL bInitialOwner,
_In_opt_ LPCSTR lpName
);
lpMutexAttributes:表示安全控制,传入NULL
bInitialOwner: bool类型,传入true表示互斥量处于未触发状态,为创建线程拥有.
lpName:设置互斥量的名称
函数原型
WINBASEAPI DWORD WINAPI WaitForSingleObject(_In_ HANDLE hHandle, _In_ DWORD dwMilliseconds);
功能:获得互斥锁
hHandle:互斥量句柄
dwMilliseconds:等待时间,一般设为INFINITE,意为永久阻塞,直到获得互斥量。
函数原型
WINBASEAPI BOOL WINAPI ReleaseMutex(_In_ HANDLE hMutex);
功能:释放互斥锁
参数:hMutex:互斥量句柄
源码
//共享资源
static int num = 0;
//子线程函数
unsigned int __stdcall ChildThreadFunc(LPVOID pM)
{
while (true)
{
Sleep(500);
num++;
printf("num:%d\n", num);
}
return 0;
}
int main()
{
HANDLE handle[5] = { 0 };
for (int i = 0; i < 5; i++)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, ChildThreadFunc, NULL, 0, NULL);
}
//阻塞等待
for (int i = 0; i < 5; i++)
{
WaitForSingleObject(handle[i], -1);
}
printf("主线程 num:%d\n", num);
getchar();
return 0;
}
结果
这是在没有添加互斥锁的情况下得到的结果,显然是错误的,我们想要的结果的应该是1,2,3,4...
错误原因分析:
1,在这段代码中子线程函数会对全局变量num进行写(num++)和读(printf)两个操作,这两个步骤有可能被打断,即但线程1对num进行写(num++)操作完后还未读时,它就失去了cpu时间碎片,线程1被挂起。这时线程2抢到了cpu时间碎片并对num进行写(num++)操作,这样num进行了两次++操作,当线程1再次获得cpu时间碎片并读(printf)取数据时就会发送错误。
2,"num++;"这个语句也不是原子操作,它分为三步走。换句话说就是他也有可能被打断,也会发生上面的错误。
第一步将num的值从内存中读取到寄存器eax中。
第二步将寄存器eax中的值与1相加,计算结果仍存入寄存器eax中。
第三步将寄存器eax中的值写回内存中。
解决思路:
只要保证线程对共享资源的操作是不会被打断的,即当一个线程对这个资源进行读写操作时,其他线程就不能对此资源操作。声明一个全局互斥锁,并初始化。在线程对共享资源操作开始时加锁,操作完成后释放即可。
正确方法
源码
//共享资源
static int num = 0;
//互斥锁
HANDLE g_Mutex = CreateMutex(NULL, FALSE, NULL);
//子线程函数
unsigned int __stdcall ChildThreadFunc(LPVOID pM)
{
while (true)
{
Sleep(500);
WaitForSingleObject(g_Mutex, INFINITE);//等待互斥量
num++;
printf("num:%d\n", num);
ReleaseMutex(g_Mutex);
}
return 0;
}
int main()
{
HANDLE handle[5] = { 0 };
for (int i = 0; i < 5; i++)
{
handle[i] = (HANDLE)_beginthreadex(NULL, 0, ChildThreadFunc, NULL, 0, NULL);
}
//阻塞等待
for (int i = 0; i < 5; i++)
{
WaitForSingleObject(handle[i], -1);
}
printf("主线程 num:%d\n", num);
getchar();
return 0;
}
结果