Window多线程同步之(互斥锁)

简述

互斥锁是用在多线程间对操作同一资源进行互斥的。一个线程占用了一个资源,那么别的线程就操作此资源,直到这个线程该释放互斥锁,其他的线程才开始可以重新抢夺这个互斥锁,成功获得互斥锁的线程利用这个资源,其他线程再次阻塞,周而复始。如对全局变量的访问,线程加锁后对变量进行读写操作,完成后释放互斥锁。比如多个线程对一个全局变量进行累加并打印。

相关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;
}

结果

Window多线程同步之(互斥锁)_第1张图片

这是在没有添加互斥锁的情况下得到的结果,显然是错误的,我们想要的结果的应该是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;
}

结果

 



你可能感兴趣的:(Windows编程)