转— 秒杀多线程第六篇 经典线程同步 事件Event

开卷本篇之前推荐阅读以下姊妹篇:

《秒杀多线程第四首
一个藏的多线程同步问题》

《秒杀多线程第五首
经典线程同步关键段CS》

 

齐同一首被利用关键段来缓解经典的多线程同步互斥问题,由于关键段的“线程所有权”特性所以要段只能用来线程的排挤而无克用于共同。本篇介绍用事件Event来尝试解决者线程同步问题。

首先介绍下什么样使事件。事件Event实际上是独基础对象,它的用非常有利于。下面列有一些常用的函数。

 

第一个 CreateEvent

函数功能:创建事件

函数原型:

HANDLECreateEvent(

 LPSECURITY_ATTRIBUTESlpEventAttributes,

 BOOLbManualReset,

 BOOLbInitialState,

 LPCTSTRlpName

);

函数说明:

首先单参数表示安全控制,一般直接传入NULL。

其次单参数确定事件是手动置位还是自动置位,传入TRUE表示手动置位,传入FALSE表示自动置位。如果为活动置位,则针对拖欠事件调用WaitForSingleObject()后会自行调用ResetEvent()使事件成为不点状态。打个细微要,手动置位事件相当给教室门,教室门而打开(被点),所以有人还得以进入直到老师去关教室门(事件成为不点发)。自动置位事件就一定给医院里撞X光的房间门,门打开后才能够进来一个口,这个人登后会见以门关上,其它食指非能够进除非门重新于打开(事件又被触发)。

老三独参数表示事件之开端状态,传入TRUR表示早已接触。

季只参数表示事件的称谓,传入NULL表示匿名事件。

 

第二个 OpenEvent

函数功能:根据名称获得一个波句柄。

函数原型:

HANDLEOpenEvent(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName    
//名称

);

函数说明:

先是单参数表示访问权限,对事件一般传入EVENT_ALL_ACCESS。详细说明可查阅MSDN文档。

次只参数表示事件句柄继承性,一般传入TRUE即可。

其三只参数表示称,不同进程面临的各个线程可以透过名称来担保它访问与一个轩然大波。

 

第三个SetEvent

函数功能:触发事件

函数原型:BOOLSetEvent(HANDLEhEvent);

函数说明:每次触发后,必来一个或者多个处等候状态下之线程变成可调度状态。

 

第四个ResetEvent

函数功能:将事件而为结尾触发

函数原型:BOOLResetEvent(HANDLEhEvent);

 

末段一个事变的清理和销毁

由事件是水源对象,因此下CloseHandle()就可就清理以及销毁了。

 

当经多线程问题屡遭安一个波和一个着重段。用事件处理主线程和子线程的一起,用要段来拍卖各子线程间的排外。详见代码:

[cpp] view
plaincopy

  1. #include <stdio.h>  
  2. #include <process.h>  
  3. #include <windows.h>  
  4. long g_nNum;  
  5. unsigned int __stdcall Fun(void *pPM);  
  6. const int THREAD_NUM = 10;  
  7. //事件和重大段  
  8. HANDLE  g_hThreadEvent;  
  9. CRITICAL_SECTION g_csThreadCode;  
  10. int main()  
  11. {  
  12.     printf(”     经典线程同步 事件Event\n”);  
  13.     printf(” — by MoreWindows( http://blog.csdn.net/MoreWindows ) –\n\n”);  
  14.     //初始化事件以及重要性段 自动置位,初始无接触的匿名事件  
  15.     g_hThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);   
  16.     InitializeCriticalSection(&g_csThreadCode);  
  17.   
  18.     HANDLE  handle[THREAD_NUM];   
  19.     g_nNum = 0;  
  20.     int i = 0;  
  21.     while (i < THREAD_NUM)   
  22.     {  
  23.         handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);  
  24.         WaitForSingleObject(g_hThreadEvent, INFINITE); //等待事件于触发  
  25.         i++;  
  26.     }  
  27.     WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);  
  28.   
  29.     //销毁事件以及主要段  
  30.     CloseHandle(g_hThreadEvent);  
  31.     DeleteCriticalSection(&g_csThreadCode);  
  32.     return 0;  
  33. }  
  34. unsigned int __stdcall Fun(void *pPM)  
  35. {  
  36.     int nThreadNum = *(int *)pPM;   
  37.     SetEvent(g_hThreadEvent); //触发事件  
  38.       
  39.     Sleep(50);//some work should to do  
  40.       
  41.     EnterCriticalSection(&g_csThreadCode);  
  42.     g_nNum++;  
  43.     Sleep(0);//some work should to do  
  44.     printf(“线程编号吧%d  全局资源值为%d\n”, nThreadNum, g_nNum);   
  45.     LeaveCriticalSection(&g_csThreadCode);  
  46.     return 0;  
  47. }  

运行结果一旦下图:

图片 1

可以扣押出来,经典线线程同步问题早就完善的解决了——线程编号的输出没有再次,说明主线程和子线程达到了一块儿。全局资源的输出是与日俱增的,说明各子线程已经互斥的造访同输出该全局资源。

 

而今咱们领略了哪运用事件,但学习就是应该要是深刻之读书,何况微软吃事件还提供了PulseEvent()函数,所以接下还持续大挖潜下事件Event,看看它还有啊秘密没。

优先来探望这个函数的本色:

第五个PulseEvent

函数功能:将事件触发后即刻用事件设置为非触及,相当给点一个风波脉冲。

函数原型:BOOLPulseEvent(HANDLEhEvent);

函数说明:这是一个请勿常用的事件函数,此函数相当给SetEvent()后及时调用ResetEvent();此时状况可以分成两种:

1.对于手动置位事件,所有正处在等候状态下线程都改成可调度状态。

2.对于自动置位事件,所有正处在等候状态下线程只来一个成可调度状态。

此后事变是终极触发的。该函数不安定,因为无法预知在调用PulseEvent
()时怎么线程正处在等候状态。

 

       下面对这个点一个事变脉冲PulseEvent ()写一个事例,主线程启动7只子线程,其中有5独线程Sleep(10)后针对同一轩然大波调用等待函数(称为快线程),另起2单线程Sleep(100)后呢针对拖欠事件调用等待函数(称为慢性线程)。主线程启动所有子线程后更Sleep(50)保证有5个赶早线程都碰巧处在等候状态中。此时要是主线程触发一个事变脉冲,那么对于手动置位事件,这5个线程都将得手施行下去。对于电动置位事件,这5只线程中会生出受一个胜利实行下去。而随便手动置位事件要机关置位事件,那2单暂缓线程由于Sleep(100)所以会见失去事件脉冲,因此慢线程都见面进去等状态而一筹莫展如愿实施下。

代码如下:

[cpp] view
plaincopy

  1. //使用PluseEvent()函数  
  2. #include <stdio.h>  
  3. #include <conio.h>  
  4. #include <process.h>  
  5. #include <windows.h>  
  6. HANDLE  g_hThreadEvent;  
  7. //快线程  
  8. unsigned int __stdcall FastThreadFun(void *pPM)  
  9. {  
  10.     Sleep(10); //用这个来确保每线程调用等待函数的顺序来肯定的随机性  
  11.     printf(“%s 启动\n”, (PSTR)pPM);  
  12.     WaitForSingleObject(g_hThreadEvent, INFINITE);  
  13.     printf(“%s 等及事件被触发 顺利完结\n”, (PSTR)pPM);  
  14.     return 0;  
  15. }  
  16. //慢线程  
  17. unsigned int __stdcall SlowThreadFun(void *pPM)  
  18. {  
  19.     Sleep(100);  
  20.     printf(“%s 启动\n”, (PSTR)pPM);  
  21.     WaitForSingleObject(g_hThreadEvent, INFINITE);  
  22.     printf(“%s 等及事件为触发 顺利竣工\n”, (PSTR)pPM);  
  23.     return 0;  
  24. }  
  25. int main()  
  26. {  
  27.     printf(”  使用PluseEvent()函数\n”);  
  28.     printf(” — by MoreWindows( http://blog.csdn.net/MoreWindows ) –\n\n”);  
  29.   
  30.     BOOL bManualReset = FALSE;  
  31.     //创建事件 第二单参数手动置位TRUE,自动置位FALSE  
  32.     g_hThreadEvent = CreateEvent(NULL, bManualReset, FALSE, NULL);  
  33.     if (bManualReset == TRUE)  
  34.         printf(“当前下手动置位事件\n”);  
  35.     else  
  36.         printf(“当前采取自动置位事件\n”);  
  37.   
  38.     char szFastThreadName[5][30] = {“快线程1000”, “快线程1001”, “快线程1002”, “快线程1003”, “快线程1004”};  
  39.     char szSlowThreadName[2][30] = {“慢线程196”, “慢线程197”};  
  40.   
  41.     int i;  
  42.     for (i = 0; i < 5; i++)  
  43.         _beginthreadex(NULL, 0, FastThreadFun, szFastThreadName[i], 0, NULL);  
  44.     for (i = 0; i < 2; i++)  
  45.         _beginthreadex(NULL, 0, SlowThreadFun, szSlowThreadName[i], 0, NULL);  
  46.       
  47.     Sleep(50); //保证快线程已经全部开行  
  48.     printf(“现在主线程触发一个事变脉冲 – PulseEvent()\n”);  
  49.     PulseEvent(g_hThreadEvent);//调用PulseEvent()就一定给以调用下面二句  
  50.     //SetEvent(g_hThreadEvent);  
  51.     //ResetEvent(g_hThreadEvent);  
  52.       
  53.     Sleep(3000);   
  54.     printf(“时间到,主线程结束运行\n”);  
  55.     CloseHandle(g_hThreadEvent);  
  56.     return 0;  
  57. }  

针对机关置位事件,运行结果如下:

图片 2

本着手动置位事件,运行结果如下:

 图片 3

 

最终总结下事件Event

1.事变是本对象,事件分为手动置位事件机关置位事件。事件Event内部它涵盖一个行使计数(所有内核对象还起),一个布尔值表示是手动置位事件或者自动置位事件,另一个布尔值用来代表事件有管接触。

2.事变可以由SetEvent()来点,由ResetEvent()来若成不点。还可由PulseEvent()来发出一个事件脉冲。

3.事变可以化解线程间同步问题,因此也能迎刃而解互斥问题。

 

末端二首《秒杀多线程第七首 经典线程同步
互斥量Mutex》和《秒杀多线程第八篇 经典线程同步 信号量Semaphore》将介绍如何行使互斥量和信号量来解决这经典线程同步问题。欢迎大家继续秒杀多线程之同。

相关文章