秒杀多线程第七篇 经典线程同步 互斥量Mutex

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

秒杀多线程第四篇一个经典的多线程同步问题

秒杀多线程第五篇经典线程同步关键段CS

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

 

前面介绍了关键段CS事件Event经典线程同步问题中的使用。本篇介绍用互斥量Mutex来解决这个问题。

互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问。互斥量与关键段的行为非常相似,并且互斥量可以用于不同进程中的线程互斥访问资源。使用互斥量Mutex主要将用到四个函数。下面是这些函数的原型和使用说明。

第一个 CreateMutex

函数功能:创建互斥量(注意与事件Event的创建函数对比)

函数原型:

HANDLECreateMutex(

  LPSECURITY_ATTRIBUTESlpMutexAttributes,

  BOOLbInitialOwner,     

  LPCTSTRlpName

);

函数说明:

第一个参数表示安全控制,一般直接传入NULL

第二个参数用来确定互斥量的初始拥有者。如果传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意味互斥量不为任何线程占用,处于触发状态。

第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名称来确保它们访问的是同一个互斥量。

函数访问值:

成功返回一个表示互斥量的句柄,失败返回NULL

 

第二个打开互斥量

函数原型:

HANDLEOpenMutex(

 DWORDdwDesiredAccess,

 BOOLbInheritHandle,

 LPCTSTRlpName     //名称

);

函数说明:

第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数表示互斥量句柄继承性,一般传入TRUE即可。

第三个参数表示名称。某一个进程中的线程创建互斥量后,其它进程中的线程就可以通过这个函数来找到这个互斥量。

函数访问值:

成功返回一个表示互斥量的句柄,失败返回NULL

 

第三个触发互斥量

函数原型:

BOOLReleaseMutex (HANDLEhMutex)

函数说明:

访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。

 

最后一个清理互斥量

由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。

 

接下来我们就在经典多线程问题用互斥量来保证主线程与子线程之间的同步,由于互斥量的使用函数类似于事件Event,所以可以仿照上一篇的实现来写出代码

[cpp]  view plain copy
  1. //经典线程同步问题 互斥量Mutex  
  2. #include <stdio.h>  
  3. #include <process.h>  
  4. #include <windows.h>  
  5.   
  6. long g_nNum;  
  7. unsigned int __stdcall Fun(void *pPM);  
  8. const int THREAD_NUM = 10;  
  9. //互斥量与关键段  
  10. HANDLE  g_hThreadParameter;  
  11. CRITICAL_SECTION g_csThreadCode;  
  12.   
  13. int main()  
  14. {  
  15.     printf("     经典线程同步 互斥量Mutex\n");  
  16.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");  
  17.       
  18.     //初始化互斥量与关键段 第二个参数为TRUE表示互斥量为创建线程所有  
  19.     g_hThreadParameter = CreateMutex(NULL, FALSE, NULL);  
  20.     InitializeCriticalSection(&g_csThreadCode);  
  21.   
  22.     HANDLE  handle[THREAD_NUM];   
  23.     g_nNum = 0;   
  24.     int i = 0;  
  25.     while (i < THREAD_NUM)   
  26.     {  
  27.         handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);  
  28.         WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被触发  
  29.         i++;  
  30.     }  
  31.     WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);  
  32.       
  33.     //销毁互斥量和关键段  
  34.     CloseHandle(g_hThreadParameter);  
  35.     DeleteCriticalSection(&g_csThreadCode);  
  36.     for (i = 0; i < THREAD_NUM; i++)  
  37.         CloseHandle(handle[i]);  
  38.     return 0;  
  39. }  
  40. unsigned int __stdcall Fun(void *pPM)  
  41. {  
  42.     int nThreadNum = *(int *)pPM;  
  43.     ReleaseMutex(g_hThreadParameter);//触发互斥量  
  44.       
  45.     Sleep(50);//some work should to do  
  46.   
  47.     EnterCriticalSection(&g_csThreadCode);  
  48.     g_nNum++;  
  49.     Sleep(0);//some work should to do  
  50.     printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_nNum);  
  51.     LeaveCriticalSection(&g_csThreadCode);  
  52.     return 0;  
  53. }  

运行结果如下图:

可以看出,与关键段类似,互斥量也是不能解决线程间的同步问题。

       联想到关键段会记录线程ID即有“线程拥有权”的,而互斥量也记录线程ID,莫非它也有“线程拥有权”这一说法。

       答案确实如此,互斥量也是有“线程拥有权”概念的。“线程拥有权”在关键段中有详细的说明,这里就不再赘述了。另外由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用ReleaseMutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程ID设置为0,并将它的递归计数器复置为0,表示这个互斥量被触发了。然后系统将公平地选定一个等待线程来完成调度(被选中的线程的WaitForSingleObject()会返回WAIT_ABANDONED_0)。

 

下面写二个程序来验证下:

第一个程序创建互斥量并等待用户输入后就触发互斥量。第二个程序先打开互斥量,成功后就等待并根据等待结果作相应的输出。详见代码:

第一个程序:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <conio.h>  
  3. #include <windows.h>  
  4. const char MUTEX_NAME[] = "Mutex_MoreWindows";  
  5. int main()  
  6. {  
  7.     HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME); //创建互斥量  
  8.     printf("互斥量已经创建,现在按任意键触发互斥量\n");  
  9.     getch();  
  10.     //exit(0);  
  11.     ReleaseMutex(hMutex);  
  12.     printf("互斥量已经触发\n");  
  13.     CloseHandle(hMutex);  
  14.     return 0;  
  15. }  

第二个程序:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. const char MUTEX_NAME[] = "Mutex_MoreWindows";  
  4. int main()  
  5. {  
  6.     HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME); //打开互斥量  
  7.     if (hMutex == NULL)  
  8.     {  
  9.         printf("打开互斥量失败\n");  
  10.         return 0;  
  11.     }  
  12.     printf("等待中....\n");  
  13.     DWORD dwResult = WaitForSingleObject(hMutex, 20 * 1000); //等待互斥量被触发  
  14.     switch (dwResult)  
  15.     {  
  16.     case WAIT_ABANDONED:  
  17.         printf("拥有互斥量的进程意外终止\n");  
  18.         break;  
  19.   
  20.     case WAIT_OBJECT_0:  
  21.         printf("已经收到信号\n");  
  22.         break;  
  23.   
  24.     case WAIT_TIMEOUT:  
  25.         printf("信号未在规定的时间内送到\n");  
  26.         break;  
  27.     }  
  28.     CloseHandle(hMutex);  
  29.     return 0;  
  30. }  

运用这二个程序时要先启动程序一再启动程序二。下面展示部分输出结果:

结果一.二个进程顺利执行完毕:

结果二.将程序一中//exit(0);前面的注释符号去掉,这样程序一在触发互斥量之前就会因为执行exit(0);语句而且退出,程序二会收到WAIT_ABANDONED消息并输出“拥有互斥量的进程意外终止”:

有这个对“遗弃”问题的处理,在多进程中的线程同步也可以放心的使用互斥量。

 

最后总结下互斥量Mutex

1.互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。

2.互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。

 

下一篇《秒杀多线程第八篇 经典线程同步 信号量Semaphore》将介绍使用信号量Semaphore来解决这个经典线程同步问题。

 

 

转载请标明出处,原文地址:http://blog.csdn.net/morewindows/article/details/7470936

如果觉得本文对您有帮助,请点击支持一下,您的支持是我写作最大的动力,谢谢。


 

122
5
主题推荐
多线程 线程 semaphore 对象 内核
猜你在找
C++学习之深入理解虚函数--虚函数表解析
手把手实现红黑树
CC++2014年7月华为校招机试真题一
cs硕士妹子找工作经历阿里人搜等互联网
我的2012-分享我的四个项目经验
string的size和length
面试经验
KMP算法原理与实现精简
比微软kinect更强的视频跟踪算法--TLD跟踪算法介绍
【精品课程】三维游戏引擎开发-渲染
【精品课程】iOS开发教程之OC语言
【精品课程】思科认证CCNPv2.0详解第3部分 IPSEC VPN技术
【精品课程】C语言入门教程
【精品课程】J2SE轻松入门第二季
id="ad_frm_0" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=4&containerId=ad_cen&frmId=ad_frm_0" style="border-width: 0px; overflow: hidden; width: 746px; height: 90px;">
查看评论
52楼  cheneagle 4天前 11:39发表 [回复]
讲得非常好,支持楼主!
51楼  xinpo66 2015-01-26 16:02发表 [回复]
楼主写的很好,这个系列让我对线程入门了。
最后的那个遗弃的处理,有些时候是我们忘记ReleaseMutex了。
50楼  dreamgis 2015-01-23 16:46发表 [回复]
谢谢楼主啊
49楼  jsdfzp 2014-12-30 15:51发表 [回复]
赞楼主!
48楼  FENGYEJINGXIANG 2014-10-19 15:59发表 [回复]
我是个小白,没有学过。看完了这篇博客后知道了互斥量不能做同步,可是却完全不知道互斥量究竟可以做什么?
47楼  ljj13223452342342 2014-05-15 16:34发表 [回复]
博主你的意思是mutex用于线程所有权因此必须由得到Mutex所有权的主线程调用ReleaseMutex才行,而Event没有所谓线程所有权因此只需在线程函数里SetEvent(),所以Mutex不能实现同步,是这样吧! 感觉受益匪浅啊!!thx
46楼  zhouda829829 2014-04-29 14:55发表 [回复]
[cpp]  view plain copy
  1. #include <pthread.h>  
  2. #include <stdio.h>  
  3. #include <windows.h>  
  4.   
  5. int g_Flag = 0;  
  6. void * thread1(void * pthreadid);  
  7. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  8.   
  9. void main(void)  
  10. {  
  11.     int a,i;  
  12.     pthread_t tid1;  
  13.     for(i=0;i<10;i++)  
  14.     {  
  15.         a = pthread_create(&tid1,NULL,thread1,(void*)i);  
  16.     }  
  17.       
  18.     getchar();  
  19. }  
  20.   
  21. void * thread1(void * pthreadid)  
  22. {  
  23.     int nThreadNum = (int) pthreadid;  
  24.     Sleep(50);//some work should to do    
  25.     pthread_mutex_lock(&mutex);  
  26.     g_Flag++;  //处理全局资源    
  27.       
  28.     Sleep(0);//some work should to do    
  29.     printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_Flag);    
  30.     pthread_mutex_unlock(&mutex);  
  31.     return 0;  
  32. }  
我就用了互斥实现了你说的要求,是不是我的理解有什么偏差啊,求指正。
Re:  ch2leo 2014-09-28 11:38发表 [回复]
回复zhouda829829:我也有同样疑问呀博主!!
zh兄的代码确实实现了同步和互斥呀!!
可以麻烦博主说明一下吗??
45楼  zhouda829829 2014-04-29 14:55发表 [回复]
[cpp]  view plain copy
  1. #include <pthread.h>  
  2. #include <stdio.h>  
  3. #include <windows.h>  
  4.   
  5. int g_Flag = 0;  
  6. void * thread1(void * pthreadid);  
  7. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  8.   
  9. void main(void)  
  10. {  
  11.     int a,i;  
  12.     pthread_t tid1;  
  13.     for(i=0;i<10;i++)  
  14.     {  
  15.         a = pthread_create(&tid1,NULL,thread1,(void*)i);  
  16.     }  
  17.       
  18.     getchar();  
  19. }  
  20.   
  21. void * thread1(void * pthreadid)  
  22. {  
  23.     int nThreadNum = (int) pthreadid;  
  24.     Sleep(50);//some work should to do    
  25.     pthread_mutex_lock(&mutex);  
  26.     g_Flag++;  //处理全局资源    
  27.       
  28.     Sleep(0);//some work should to do    
  29.     printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_Flag);    
  30.     pthread_mutex_unlock(&mutex);  
  31.     return 0;  
  32. }  
我就用了互斥实现了你说的要求,是不是我的理解有什么偏差啊,求指正。
44楼  zhouda829829 2014-04-29 14:55发表 [回复]
[cpp]  view plain copy
  1. #include <pthread.h>  
  2. #include <stdio.h>  
  3. #include <windows.h>  
  4.   
  5. int g_Flag = 0;  
  6. void * thread1(void * pthreadid);  
  7. static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;  
  8.   
  9. void main(void)  
  10. {  
  11.     int a,i;  
  12.     pthread_t tid1;  
  13.     for(i=0;i<10;i++)  
  14.     {  
  15.         a = pthread_create(&tid1,NULL,thread1,(void*)i);  
  16.     }  
  17.       
  18.     getchar();  
  19. }  
  20.   
  21. void * thread1(void * pthreadid)  
  22. {  
  23.     int nThreadNum = (int) pthreadid;  
  24.     Sleep(50);//some work should to do    
  25.     pthread_mutex_lock(&mutex);  
  26.     g_Flag++;  //处理全局资源    
  27.       
  28.     Sleep(0);//some work should to do    
  29.     printf("线程编号为%d  全局资源值为%d\n", nThreadNum, g_Flag);    
  30.     pthread_mutex_unlock(&mutex);  
  31.     return 0;  
  32. }  
我就用了互斥实现了你说的要求,是不是我的理解有什么偏差啊,求指正。
43楼  第XXX个小号 2014-03-07 18:10发表 [回复]
已经反复看了楼主文章好几遍,每次看完文章后,最大的感悟 是,是时候认真学习英文了
42楼  liuphahaha 2014-01-10 11:01发表 [回复]
博主,关于同步和互斥 ,这两个概念,我有点模糊,能否讲明白些?? 谢谢!!
41楼  JulianShang 2013-10-31 18:33发表 [回复]
谢谢分享这么好的资料!博主也是电子科技大学的啊?
Re:  MoreWindows 2013-10-31 21:04发表 [回复]
回复JulianShang:是的,小硕。
40楼  yy610879290 2013-10-26 11:37发表 [回复]
楼主互斥量的例子有问题:
1、首先,拿到互斥量的线程是主线程,在子线程里面去ReleaseMutex肯定失败,返回的是FALSE。
2、这里创建的互斥量没有任何作用,while循环里面的WaitForSingleObject发现访问互斥量的线程是同一个线程,所以会把当前线程置为可调度的。
Re:  MoreWindows 2013-10-31 21:05发表 [回复]
回复yy610879290:对的,文章就是为了证明互斥量不能这样用。
39楼  longzixiao 2013-09-29 20:11发表 [回复]
关于楼主的这几篇文章,都仔细看过,但觉得有些地方不妥啊。。。
对这篇文章,只用用互斥量明显可以做到线程间的同步及资源的互斥访问,为什么还要用关键段呢??还有,为什么楼主就说互斥量不能解决线程间的同步问题呢?或者是我对线程的认识不深吧,还望楼主给予解答吧
38楼  lmnxjf 2013-09-16 10:30发表 [回复]
多线程还是得多练习啊, 谢谢
37楼  wdmx 2013-08-26 21:23发表 [回复]
mark 一下
36楼  绝杀fc小飞侠 2013-08-22 15:07发表 [回复]
本文说明mutex不能用于同步,如果要用他实现互斥,该怎么写?
Re:  MoreWindows 2013-08-23 19:10发表 [回复]
回复allen_fan_11:WaitForSingleObject + ReleaseMutex
35楼  byawdd 2013-08-19 09:11发表 [回复]
如果说,我不像楼主那样在主线程中多次调用waitfor...:
while (i < THREAD_NUM) 

handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); 
WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被触发 
i++; 

只是在各个线程中使用一次waitforsingleobject(hMutex, INFINIT),并没有任何的重复使用,就像没有用楼主的while循环来wait,是不是也可以达到线程同步的效果呢...
Re:  TCSS001 2015-04-03 10:21发表 [回复]
回复byawdd:楼主的线程互斥是用互斥区域实现的,那个互斥对象是没有用的。你的想法是用互斥对象实现互斥,效果是一样的。
34楼  风风清清扬扬 2013-08-07 17:24发表 [回复]
引用“hujintao2009”的评论:博主,变量命名中的那个g代表什么意思呢

global?
Re:  lynnbest 2013-08-16 19:58发表 [回复]
回复hujintao2009:确实是全局变量的意思..
33楼  风风清清扬扬 2013-08-07 17:23发表 [回复]
博主,变量命名中的那个g代表什么意思呢
32楼  RMB7722 2013-07-18 23:46发表 [回复]
看了之后,还是不懂怎样用在项目中,怎么办
31楼  其实我很菜 2013-07-18 23:24发表 [回复]
楼主,我觉得这句“WaitForSingleObject(g_hThreadParameter, INFINITE); ”放在线程函数代码执行前,“ReleaseMutex”放在线程函数代码结束后更能让人理解些!不知道说的对否
Re:  其实我很菜 2013-07-27 16:07发表 [回复]
回复yuanyuanzhouyuan:又仔细看了一遍楼主的博客,然后将评论也一一看了,自己的疑问都在评论里面找到了,对多线程的知识有了本质上的认识,顶楼主^_^
30楼  亚细亚 2013-06-13 10:40发表 [回复]
[cpp]  view plain copy
  1. HANDLE WINAPI CreateMutex(  
  2.   _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,  
  3.   _In_      BOOL bInitialOwner,  
  4.   _In_opt_  LPCTSTR lpName  
  5. );  
  6.  lpMutexAttributes:互斥量安全访问属性,一般置为NULL;  
  7.   
  8. bInitialOwner:控制互斥量的初始状态,一般设置为false,是互斥量的线程ID和递归计数都设为0,表示互斥量不被任何进程占用,处于触发状态,其他进程可以进行调用等待函数,获得该互斥量对象,获得对同步资源的占用。如果初始值设为true,互斥量的线程ID设置为当前线程,递归计数设为1,表示当前线程占用互斥量对象,拥有对同步资源的独占,互斥量处于未触发状态。  
  9.   
  10. lpName:用于创建有名的内核对象,即用来创建跨进程边界的内核对象。  
  11.   
  12. CreateMutex用于创建名为lpName的互斥量内核对象,并返回指向该内核对象的句柄。如果该内核对象已经存在,那么CreateMutex会返回该内核对象的句柄,并通过系统返回错误ERROR_ALREADY_EXISTS,通过GetLastError()获得。  
Re:  亚细亚 2013-06-13 10:42发表 [回复]
回复yaxiya:刚开始对触发和未触发理解比较模糊,现在明白了^_^
Re:  亚细亚 2013-06-13 10:45发表 [回复]
回复yaxiya:未触发其实就是已经有线程独占了,所以其它线程就只能等待;当该线程释放互斥量后,也就是互斥量中的线程ID为0,计数器为0,这时,互斥量处于触发状态,其它线程可以进入,从而拥有该互斥量对象了;
29楼  jiayan1990 2013-05-30 21:19发表 [回复]
最近在看楼主的这一系列文章,受益匪浅啊。我有一个问题:楼主后面讲的进程一和进程二这个测试程序我运行了一下,结果不管我加没加那句exit(0),程序的运行结果都是拥有互斥量的进程意外终止,这是怎么回事?
28楼  jianxianbai 2013-04-17 15:40发表 [回复]
我觉得这样理解比较好,mutex和关键段就像一张房卡(或者通行证),每扇门只能有一张房卡(每个关卡只有一张通行证)。每个线程就像一个人,只有拿到房卡(通行证)的人可以进入门中(通过关卡),wait。。。。(ENTER...)函数就相当于申请房卡(通行证),ReleaseMutex (leave。。。)就像归还房卡(通行证),但是没有申请到房卡的人是无法归还房卡的,申请到房卡的人就一直拥有了这张房卡了,直到归还为止。

而他们的区别就在于,如果把某个用户比作一个国家。mutex更像是某个关卡的通行证,是属于整个国家的,在整个国家都需要这张通行证才能过这个关卡。而如果把每个进程比作一个国家里的一座座大楼,那关键段就像是真正的房卡,房卡在不同的大楼里面可以重名。A楼的卡也不会影响B楼的卡
Re:  jianxianbai 2013-04-17 15:42发表 [回复]
回复jianxianbai:而管理一座楼的房间的开销显然比管理一个关卡的开销要小得多
27楼  xiaocai0807 2013-04-16 15:13发表 [回复]
l楼主,写得非常不错,能不能把多线程这一系列的文章整理下供我们下载学习,非常感谢
Re:  MoreWindows 2013-04-16 18:01发表 [回复]
回复caimengru:这个系列还有很多篇没写完了,写完再来整理下。
26楼  sayaxiaopan 2013-03-22 13:29发表 [回复]
学习学习再学习
25楼  zoopang 2013-03-20 22:29发表 [回复]
支持一下!收藏才是王道!
24楼  wzy2006 2013-03-20 15:42发表 [回复]
太好了,我这菜鸟刚好用得到,多谢楼主,希望继续更新。支持支持
23楼  小九 2012-12-19 20:04发表 [回复]
兄弟这个系列写的不错。比我强多了。正巧我最近在看windows核心编程。有点问题说一下。
正如之前回复中有人提到的,例子一确实有问题,它无法作为mutex不能进行同步的例证,因为你那么写代码跟没用Mutex一模一样。
1、你主线程创建的互斥量本身就是已触发状态。因此主线程中的第一次Wait时直接将mutex归属于主线程,然后主线程继续向下执行,下次在Wait时也无需等待,因为此mutex归属于主线程,直接计数加一后向下执行。而新生线程中的release无作用,它本身就没占有过互斥量,怎么能release呢,所以返回fasle向下执行。因此可能出现创建了一个子线程后,子线程尚未运行任何代码时,你主线程又创建了多个子线程。当这些子线程运行代码时,取了重复的i值。
2、信号量的时候这么做就可以了。因为信号量不归属于任何线程,所以你那么编码保证了主线程创建一个子线程->主线程等待->子线程取i值->主线程创建另一个子线程->主线程等待->子线程取的新i值的有序循环。
3、当然,为了实现你这种有序的循环,靠mutex确实做不到。就算正确的在同一个线程中对mutex进行wait和release,依旧只能保证一次只有一个线程处理临界资源i而不能保证次序。
你最后总结的时候我觉得是对的。mutex的线程相关性导致无法进行线程同步。其实名字翻译过来本身就是互斥量。不能同步线程的次序也是题中应有之意。
Re:  jianxianbai 2013-04-17 15:28发表 [回复]
回复windyitian:楼主的意思就是要说明,这个mutex是属于某个线程的这个结论。明白吗?.......
Re:  ENIAC初学者 2013-10-18 15:39发表 [回复]
回复jianxianbai:我有同样的疑问
g_hThreadParameter = CreateMutex(NULL, FALSE, NULL); 楼主此处初始化有问题吧,第二个参数为FALSE表示互斥量对象的线程ID和递归计数都将被设为0,这意味着互斥量不为任何线程占用,因此处于触发状态,既然是触发状态那接下来在线程中调用的ReleaseMutex释放触发状态不就是多余了吗,因此第二个参数应该是TRUE,处于未触发状态才对吧
22楼  linrulei11 2012-10-23 15:30发表 [回复]
讲解的很深入,学习了……
21楼  小强_加油 2012-08-16 18:30发表 [回复]
LZ线程创建之后会立即执行吗,还是通过WaitForSingleObject函数,等创建好的线程执行完线程内的函数后,才创建下一个
Re:  MoreWindows 2012-08-19 19:30发表 [回复]
回复aini201:线程在创建后会不会立即执行,取决你创建线程时传入的参数。请看http://blog.csdn.net/morewindows/article/details/7421759
Re:  小强_加油 2012-08-19 21:25发表 [回复]
回复MoreWindows:谢谢楼主的回复。
你在这里所说的参数是是指的,什么参数啊。比如在这个章节中,指的是 g_hThreadParameter吗?
Re:  MoreWindows 2012-08-20 10:54发表 [回复]
回复aini201:本篇里面的 WaitForSingleObject(g_hThreadParameter, INFINITE); 是等待互斥量的触发,如触发了才能继续执行后面的代码。
如果你将本文第一个例子中:
25. while (i < THREAD_NUM) 
26. { 
27. handle = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); 
28. WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被触发 
29. i++; 
30. } 
的第28句WaitForSingleObject(g_hThreadParameter, INFINITE); 
改成
WaitForSingleObject( handle[i] ); 
那么主线程就会等待上一个子线程执行完毕才开始启动下一个子线程。
Re:  MoreWindows 2012-08-20 10:56发表 [回复]
回复MoreWindows:我晕。。。这CSDN评论针将“[ ”和“]” 给过滤掉。。。
Re:  MoreWindows 2012-08-20 10:53发表 [回复]
回复aini201:是CreateThread()的参数,第五个参数指定额外的标志来控制线程的创建,为0表示线程创建之后立即就可以进行调度,如果为CREATE_SUSPENDED则表示线程创建后暂停运行,这样它就无法调度,直到调用ResumeThread()。
20楼  futurepi 2012-06-12 10:39发表 [回复]
很不错,继续学习
19楼  kui606 2012-06-06 09:05发表 [回复]
good,学习了
18楼  wave426 2012-05-31 15:41发表 [回复]
非常感谢,讲解得很清晰明了
17楼  dyingfair 2012-05-20 23:52发表 [回复]
谢谢楼主,期待你本专栏的最后两篇。
16楼  cwq0421 2012-05-09 10:16发表 [回复]
请赶紧更新吧
Re:  MoreWindows 2012-05-09 11:00发表 [回复]
回复cwq0421:已经更新了二篇。
15楼  yanhuangtxz 2012-05-07 18:04发表 [回复]
35. DeleteCriticalSection(&g_csThreadCode); 
36. for (i = 0; i < THREAD_NUM; i++) 
37. CloseHandle(handle[i]); 
前面的事件也是有很多线程的,但在最后没有循环释放,这里的互斥锁怎么就有循环释放了,不是很理解。
难道是事件自动释放了,而互斥需要手动? 
楼主大大,是这样子吗 ?
Re:  MoreWindows 2012-05-07 19:10发表 [回复]
回复yanhuangtxz:36. for (i = 0; i < THREAD_NUM; i++) 
37. CloseHandle(handle); 
这里是释放线程句柄,回收子线程资源,如果不显式写出,那么主线程将执行return 0;而导致进程终止,而进程一旦终止,系统会自动回收各线程的资源。所以也不会导致资源泄露。
当然严格来讲,在代码中显式写出还是好一点。
Re:  小强_加油 2012-08-16 17:06发表 [回复]
回复MoreWindows:释放句柄的时候,相对应的句柄的内存会变成NULL吗?
14楼  tjwqyss371732946 2012-04-28 22:39发表 [回复]
先加关注了
13楼  lfh05 2012-04-28 16:09发表 [回复]
《windows 核心编程》的代码中文版?
12楼  xaobaobao 2012-04-27 22:08发表 [回复]
还是不懂线程啊。
11楼  xujian3605 2012-04-24 13:25发表 [回复]
小白表示感谢,持续关注中
Re:  MoreWindows 2012-04-26 12:17发表 [回复]
回复xujian3605:谢谢支持,后面几篇也会陆续发布。
10楼  SpbDev 2012-04-24 11:58发表 [回复]
Mutex的进程间同步,仅限于同一个windows登录用户启动的进程,如果是不同用户启动的(比如控制台用户和远程桌面用户),进程间用Mutex是无效的
Re:  chmlqw 2012-04-25 20:20发表 [回复]
回复SpbDev:MSDN上有解释,如果要创建全局的,名字前面加globe。所有用户都有效。 The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session name space.
Re:  MoreWindows 2012-04-26 16:47发表 [回复]
回复chmlqw:正解。
9楼  漠漠兔八哥 2012-04-22 17:04发表 [回复]
支持楼主啊~深入浅出~
8楼  好人吗 2012-04-20 22:52发表 [回复]
对我们这些小白还是很有帮助的。非常改写楼主的奉献
Re:  MoreWindows 2012-04-21 15:09发表 [回复]
回复down_login:呵呵,也谢谢你,你们的支持是我写作最大的动力。
7楼  MoreWindows 2012-04-20 15:16发表 [回复]
回复xianyunhe1234:。。。
我用例子一就是为了证明互斥量不能用于同步问题,文章后面还分析了其原因。
Re:  qinfengxiaoyue 2012-04-21 18:27发表 [回复]
回复MoreWindows:例子一对mutex的使用欠妥当。同意5楼同学的说法。以下是MSDN对mutex使用的一些说明。
The state of a mutex object is signaled when it is not owned by any thread. The creating thread can use the bInitialOwner flag to request immediate ownership of the mutex. Otherwise, a thread must use one of the wait functions to request ownership. When the mutex's state is signaled, one waiting thread is granted ownership, the mutex's state changes to nonsignaled, and the wait function returns. Only one thread can own a mutex at any given time. The owning thread uses the ReleaseMutex function to release its ownership.
Re:  MoreWindows 2012-04-21 20:05发表 [回复]
回复qinfengxiaoyue:文章前面其实也说明了:
第三个触发互斥量
函数原型:
BOOLReleaseMutex (HANDLEhMutex)
函数说明:
访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束访问,其它线程可以开始访问了。
例一本来也就是为了直观形象的证明其不能用于同步,所以就违反这一规定了来试了一下。
6楼  jianshiku 2012-04-20 10:35发表 [回复]
thanks
5楼  xianyunhe1234 2012-04-19 09:55发表 [回复]
楼主的例子1中并没有正确的使用Mutex,所有的子线程并没有使用 WaitForSingleObject来获得对Mutex的拥有权。所有的ReleaseMutex的也就完全没有效果,ReleaseMutex返回的全是false。
Re:  少年弱则中国弱 2012-08-13 14:34发表 [回复]
引用“xianyunhe1234”的评论:楼主的例子1中并没有正确的使用Mutex,所有的子线程并没有使用 WaitForSingleObje...

我同意,在MSDN中的xxxxMutex相关的说明中很清楚了,你这个线程都没有wait到它,所有权都不是该线程,Release是没用的。
代码拷贝了下来,略作修改,可以使用的。另外那个cs可以去掉了。
Re:  MoreWindows 2012-04-19 20:17发表 [回复]
回复xianyunhe1234:那例子一中应该如何来使用Mutex了?
Re:  xianyunhe1234 2012-04-20 09:03发表 [回复]
回复MoreWindows:每一个线程要想通过mutex进入互斥区,必须使用WaitForSingleObject来加锁,然后 使用ReleaseMutex来解锁,一般情况下必须成对出现,否则互斥就无效了。例子1中的WaitForSingleObject只对主线程有效,对子线程无效。
Re:  MoreWindows 2012-04-20 10:14发表 [回复]
回复xianyunhe1234:"例子1中的WaitForSingleObject只对主线程有效,对子线程无效。"那是因为互斥量的线程所有权的原因。不是WaitForSingleObject要与WaitForSingleObject必须成对的原因。
Re:  chmlqw 2012-04-19 21:43发表 [回复]
[cpp]  view plain copy
  1. int main(void)  
  2. {  
  3.   //前面的省略  
  4.   while (i < THREAD_NUM)   
  5.   {  
  6.     handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);  
  7.     WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被触发  
  8.     ReleaseMutex(g_hThreadParameter);  
  9.     i++;  
  10.   }  
  11.   // 后面也省略  
  12. }  
  13. unsigned int __stdcall Fun(void *pPM)  
  14. {  
  15.   int nThreadNum = *(int *)pPM;  
  16.   
  17.   WaitForSingleObject(g_hThreadParameter, INFINITE);  
  18.   ReleaseMutex(g_hThreadParameter);//触发互斥量  
  19.   
  20.   // 后面省略  
  21.   return 0;  
  22. }  
他说的应该是这个意思,但是这样的话,主线程和子线程获取mutex的机会是一样的,所以有可能主线程先获取了mutex,那么就无法达到预期的要求了。所以mutex不方便用于同步。不像EVENT,可以确保子线程先得到信号。
Re:  xianyunhe1234 2012-04-20 09:03发表 [回复]
回复chmlqw:嗯,是这个意思。
Re:  MoreWindows 2012-04-20 10:16发表 [回复]
回复xianyunhe1234:17. WaitForSingleObject(g_hThreadParameter, INFINITE); 
18. ReleaseMutex(g_hThreadParameter);//触发互斥量 
先加锁然后什么也不做又马上解锁?
Re:  chmlqw 2012-04-20 18:52发表 [回复]
回复MoreWindows:WaitForSingleObject的作用是想等待子线程获取了变量值,然后主线程在继续下一个线程的创建。所以得到以后,就可以马上释放了,只有这里释放了,子线程才有机会获取。但是我前面也讲了,因为Mutex的特殊性(就是主线程和子线程wait之后,获取信号的机会是一样的),所以无法达到同步的目的,只能用来互斥。
Re:  xianyunhe1234 2012-04-20 14:20发表 [回复]
回复MoreWindows:仅使用mutex无法实现同步,我是同意的。
4楼  artwalkx 2012-04-19 09:38发表 [回复]
mark it.
3楼  chmlqw 2012-04-18 20:36发表 [回复]
一直以为Mutex和Event没啥差别,今天终于懂了。Mutex有线程拥有权,在同一个线程中可以多次调用 WaitForSingleObject,而Event却不行。 所以不能用MUTEX来同步。 非常感谢LZ
Re:  MoreWindows 2012-04-18 20:47发表 [回复]
回复chmlqw:呵呵,不客气,本博客欢迎任何技术讨论。
2楼  oldworm 2012-04-18 12:14发表 [回复]
我提几点不同看法:
1、秒杀xxxx,这个题目有噱头之嫌疑。
2、只关注win的多线程缺少了太多,win上其实缺乏了一点东西,如条件变量读写锁等vista之后才加上去的,用win来讲这个东西不是最合适的。
3、其实现在c++11已经出了标准,多线程基础设施相关部分已很完善了,你还在win的api级别,这个立意很低了。
Re:  小强_加油 2012-08-16 18:32发表 [回复]
回复oldworm:哥们,无论怎么说。楼主对我们这些对线呈不了解的能帮助很大的。
Re:  MoreWindows 2012-04-18 18:57发表 [回复]
回复oldworm:1。这个“有噱头之嫌疑”,这个是我面试的亲身经历,面试时只要问到多线程,这轮面试就能顺利通过了。我也希望读者们看后能够顺利进行秒杀多线程面试题。因此你可以将这个标题看作是我对读者的祝愿。
2。条件变量读写锁后面有有介绍的。
3。基础扎实才是王道,你说的这些“多线程基础设施”不也要微软的程序员去编写代码吗?
1楼  zlevel 2012-04-18 09:58发表 [回复]
呵呵,确实讲解的细致,先收藏了。
多谢LZ。
Re:  MoreWindows 2012-04-18 18:37发表 [回复]
回复zlevel:谢谢支持
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值