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

转载出处:秒杀多线程第六篇 经典线程同步 事件Event


学习笔记:

这篇文章已经介绍的十分详细,此处记录一下看这篇文章时产生的疑问和对该疑问的解答。

关键段来处理各子线程间的互斥。详见代码:

[cpp]  view plain  copy
  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;  
看了这个代码,我的第一感觉就是当进入Fun时37行就触发这个事件了,那么此时24行的 WaitForSingleObject(g_hThreadEvent, INFINITE)将返回接着就执行++i,那么问题来了,当Fun函数耗时较长时那岂不是会执行多次++i而  g_nNum++只执行一次???我这时的想法就是把37行SetEvent(g_hThreadEvent)放在return的前面,这样就不会出现这种情况了。最后看了一下该文章的评论,得到启发,如果将37行放在return前面的话这样对于线程的并发效率来说就会收到影响,所以不能这样更改。

那么怎样解决这个问题呢,后来自己跑了一下这个程序,然后分析了一下,发现这个想法是完全不对的。下面就来说说此段代码是怎样执行的。当进入Fun时触发事件,执行++i;我们考虑一种极端的情况,当while退出时,即i=10时,第一次执行Fun还没有退出,根据上述我的想法的话确实是这样的,但是我之前的想法是i=10,而g_nNum=1整个代码就执行完了,而不会有线程编号和全局资源的值一一对应了,后来发现自己忽略了该代码中还有关键段的使用和 WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE)的使用,而每次进入Fun时且在触发时间之前 int nThreadNum = *(int *)pPM已经将线程编号赋给了nThreadNum ,我们这样理解,该代码中new了10个线程,每个线程执行线程函数Fun,我们把这个Fun编号,即线程1对应Fun1,线程2对应Fun2,这样每个线程就对应了一个nThreadNum ,虽然i=10,而g_nNum++只执行了一次,但是new了10个线程,且每个线程都执行了int nThreadNum = *(int *)pPM,只是g_nNum++在关键段中,所以其他线程处于等待状态,当线程1对应的Fun1执行完后,其他线程就get到该资源处于空闲状态,然后其余线程中某个线程执行其对应的Fun。因为其他线程都在不断的get这个资源的状态,所以当线程1返回时我们不能确定是那个线程的EnterCriticalSection返回了,则就出现了线程编号和全局资源的值不是递增的对应,又由于WaitForMultipleObjects的作用,使得会执行10次Fun。

    根据以上分析,可知该篇中博主的代码还是比较合理的,不过想让线程编号和全局资源的值递增的对应的话这样是不能实现的,那么要怎样实现递增对应呢???这个我也不知道了,后续学习中如果有办法解决这个问题将继续更新!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值