您的位置:首页 > 其它

使用互斥量进行线程同步,与关键段和事件的区别

2012-12-15 01:56 393 查看
#include <iostream>
#include <process.h>
#include <windows.h>
#include <string>
using std::cout;
using std::endl;
using std::string;

const int num=2;
int count;
unsigned __stdcall ThreadFun(void* par);

int main()
{
count=0;
HANDLE handles[num];
for(int i=0;i<num;++i)
{
handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0);
}
for(int i=0;i<num;++i)
CloseHandle(handles[i]);
system("PAUSE");
return 0;
}
unsigned __stdcall ThreadFun(void* par)
{
Sleep(1000);
for(int i=0;i<10;++i)
cout<<"cout:"<<++count<<endl;
count=0;
return 0;
}




上面的代码在多线程环境中会争夺执行,导致输出结果不是预期的,下面我们先用互斥量来完善下

互斥量包含计数,线程ID,递归计数,互斥量与关键段的行为完全相同,互斥量是内核对象,而关键段是用户模式(对资源争夺激烈会导致等待关键段的线程进入内核模式等待),重点就是线程ID和递归计数。
这2个字段导致互斥量的行为和关键段相似,和事件内核对象不同。

#include <iostream>
#include <process.h>
#include <windows.h>
#include <string>
using std::cout;
using std::endl;
using std::string;

const int num=2;
int count;
HANDLE ThreadMutex;
unsigned __stdcall ThreadFun(void* par);

int main()
{
count=0;
HANDLE handles[num];
//互斥量对象的线程ID和递归计数初始化为0,互斥量想在不为任何线程占用
ThreadMutex=CreateMutex(NULL,FALSE,NULL);
for(int i=0;i<num;++i)
{
handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0);
}
//等待所有线程执行完毕
WaitForMultipleObjects(num,handles,TRUE,INFINITE);
for(int i=0;i<num;++i)
CloseHandle(handles[i]);
CloseHandle(ThreadMutex);
system("PAUSE");
return 0;
}
unsigned __stdcall ThreadFun(void* par)
{
//等待互斥量对象(内部检查互斥量对象的线程ID是是否为0,0为触发状态)
//如果线程ID不为0,那么调用线程将进入等待状态
WaitForSingleObject(ThreadMutex,INFINITE);
for(int i=0;i<10;++i)
cout<<"cout:"<<++count<<endl;
count=0;
//释放对资源的所有权,将互斥量对象的线程ID和递归计数设置成0
ReleaseMutex(ThreadMutex);
return 0;
}




结果和我们预期的一样了,从上面代码感觉互斥量和事件对象很像,但是事实却不是这样,下面我们分析下互斥量,关键段,事件这3个的区别。

稍微修改下代码,将WaitForMultipleObjects(num,handles,TRUE,INFINITE);放到创建线程之前,然后在线程方法里面触发互斥量。

#include <iostream>
#include <process.h>
#include <windows.h>
#include <string>
using std::cout;
using std::endl;
using std::string;

const int num=2;
int count;
HANDLE ThreadMutex;
unsigned __stdcall ThreadFun(void* par);

int main()
{
count=0;
HANDLE handles[num];
//互斥量对象的线程ID和递归计数初始化为0,互斥量想在不为任何线程占用
ThreadMutex=CreateMutex(NULL,FALSE,NULL);
for(int i=0;i<num;++i)
{
WaitForSingleObject(ThreadMutex,INFINITE);
handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0);
}
//等待所有线程执行完毕
WaitForMultipleObjects(num,handles,TRUE,INFINITE);
for(int i=0;i<num;++i)
CloseHandle(handles[i]);
CloseHandle(ThreadMutex);
system("PAUSE");
return 0;
}
unsigned __stdcall ThreadFun(void* par)
{
//等待互斥量对象(内部检查互斥量对象的线程ID是是否为0,0为触发状态)
//如果线程ID不为0,那么调用线程将进入等待状态
//WaitForSingleObject(ThreadMutex,INFINITE);
for(int i=0;i<10;++i)
cout<<"cout:"<<++count<<endl;
count=0;
//释放对资源的所有权,将互斥量对象的线程ID和递归计数设置成0
cout<<"ReleaseMutex:"<<ReleaseMutex(ThreadMutex)<<endl;
return 0;
}




结果不是预期的,输出的ReleaseMutex方法的调用结果为0(FALSE),ReleaseMutex方法调用失败,原因是互斥量和关键段一样都有线程所有权的概念,互斥量和关键段都是绑定到执行线程。

互斥量的WaitForSingleObject-->ReleaseMute,关键段的EnterCriticalSection-->LeaveCriticalSection,都是必须在同一个线程内执行,线程A拥有了资源的所有权,那么释放所有权也必须由线程A来执行,所以上面的代码ReleaseMute方法执行失败,因为WaitForSingleObject在主线程内执行的,所以互斥量的线程ID字段的值就是主线程的ID,而方法线程来执行ReleaseMute就会失败,下面是关键段的实现互斥的代码。

unsigned __stdcall ThreadFun(void* par)
{
EnterCriticalSection(&cs);
for(int i=0;i<10;++i)
cout<<"cout:"<<++count<<endl;
count=0;
LeaveCriticalSection(&cs);
return 0;
}


而事件内核对象就比较灵活,互斥量和关键段都是线程互斥(当资源可用,所有等待的线程无法按照顺序来执行,由系统决定线程所有权),而事件是同步(可以控制线程的执行顺序),事件内核对象可以在主线程调用WaitForSingleObject来等待事件对象激活,在执行线程中设置对象的状态(SetEvent,ResetEvent)。

下面用事件来实现上面代码的同步

#include <iostream>
#include <process.h>
#include <windows.h>
#include <string>
using std::cout;
using std::endl;
using std::string;

const int num=2;
int count;
HANDLE ThreadEvent;
unsigned __stdcall ThreadFun(void* par);

int main()
{
count=0;
HANDLE handles[num];
//创建自动重置,未激活的事件内核对象
ThreadEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
for(int i=0;i<num;++i)
{
//创建执行线程,顺利创建第一个线程,然后程序会在WaitForSingleObject处等待,
//直到第一个线程将事件状态设置为激活状态,才能继续下去
handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0);
//注意:主线程内等待事件被激活
WaitForSingleObject(ThreadEvent,INFINITE);
//这里会自动调用ResetEvent()将事件设置为未激活
}
//等待所有线程执行完毕
WaitForMultipleObjects(num,handles,TRUE,INFINITE);
for(int i=0;i<num;++i)
CloseHandle(handles[i]);
CloseHandle(ThreadEvent);
system("PAUSE");
return 0;
}
unsigned __stdcall ThreadFun(void* par)
{
for(int i=0;i<10;++i)
cout<<"cout:"<<++count<<endl;
count=0;
//设置事件为激活状态,这里是执行线程
cout<<"ReleaseMutex:"<<SetEvent(ThreadEvent)<<endl;
return 0;
}


结果和预期的一样,就不上图了,SetEvent方法执行结果也是TRUE。

本文版权归kennyMc和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐