使用互斥量进行线程同步,与关键段和事件的区别
2012-12-15 01:56 kennyMc 阅读(2749) 评论(0) 收藏 举报1 #include <iostream> 2 #include <process.h> 3 #include <windows.h> 4 #include <string> 5 using std::cout; 6 using std::endl; 7 using std::string; 8 9 const int num=2; 10 int count; 11 unsigned __stdcall ThreadFun(void* par); 12 13 int main() 14 { 15 count=0; 16 HANDLE handles[num]; 17 for(int i=0;i<num;++i) 18 { 19 handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0); 20 } 21 for(int i=0;i<num;++i) 22 CloseHandle(handles[i]); 23 system("PAUSE"); 24 return 0; 25 } 26 unsigned __stdcall ThreadFun(void* par) 27 { 28 Sleep(1000); 29 for(int i=0;i<10;++i) 30 cout<<"cout:"<<++count<<endl; 31 count=0; 32 return 0; 33 }

上面的代码在多线程环境中会争夺执行,导致输出结果不是预期的,下面我们先用互斥量来完善下
互斥量包含计数,线程ID,递归计数,互斥量与关键段的行为完全相同,互斥量是内核对象,而关键段是用户模式(对资源争夺激烈会导致等待关键段的线程进入内核模式等待),重点就是线程ID和递归计数。
这2个字段导致互斥量的行为和关键段相似,和事件内核对象不同。
1 #include <iostream> 2 #include <process.h> 3 #include <windows.h> 4 #include <string> 5 using std::cout; 6 using std::endl; 7 using std::string; 8 9 const int num=2; 10 int count; 11 HANDLE ThreadMutex; 12 unsigned __stdcall ThreadFun(void* par); 13 14 int main() 15 { 16 count=0; 17 HANDLE handles[num]; 18 //互斥量对象的线程ID和递归计数初始化为0,互斥量想在不为任何线程占用 19 ThreadMutex=CreateMutex(NULL,FALSE,NULL); 20 for(int i=0;i<num;++i) 21 { 22 handles[i]=(HANDLE)_beginthreadex(NULL,0,ThreadFun,NULL,0,0); 23 } 24 //等待所有线程执行完毕 25 WaitForMultipleObjects(num,handles,TRUE,INFINITE); 26 for(int i=0;i<num;++i) 27 CloseHandle(handles[i]); 28 CloseHandle(ThreadMutex); 29 system("PAUSE"); 30 return 0; 31 } 32 unsigned __stdcall ThreadFun(void* par) 33 { 34 //等待互斥量对象(内部检查互斥量对象的线程ID是是否为0,0为触发状态) 35 //如果线程ID不为0,那么调用线程将进入等待状态 36 WaitForSingleObject(ThreadMutex,INFINITE); 37 for(int i=0;i<10;++i) 38 cout<<"cout:"<<++count<<endl; 39 count=0; 40 //释放对资源的所有权,将互斥量对象的线程ID和递归计数设置成0 41 ReleaseMutex(ThreadMutex); 42 return 0; 43 }

结果和我们预期的一样了,从上面代码感觉互斥量和事件对象很像,但是事实却不是这样,下面我们分析下互斥量,关键段,事件这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和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
浙公网安备 33010602011771号