您的位置:首页 > 其它

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

2017-12-22 14:41 155 查看

简述

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

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


结果


 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息