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和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。