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

来源:互联网 发布:java获取系统当前年份 编辑:程序博客网 时间:2024/05/29 08:52

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

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

秒杀多线程第五篇经典线程同步关键段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 plaincopy
  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 plaincopy
  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 plaincopy
  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

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 蹲式厕所被袜子堵了怎么办 自热包的水喝了怎么办 火锅发热包不小心吃了怎么办 塑料螺旋饭盒盖子被吸住了怎么办 昨晚喝太多酒今天排尿拍不出怎么办 开光过的貔貅摔坏一点嘴巴怎么办 诺基亚6第二代忘记解屏密码怎么办 工厂搬迁已经般空了工人怎么办 被上司强行换了一个岗位该怎么办 上司要调整我岗位我该怎么办 我被别人打伤了警察不管怎么办 郑州共享汽车小黄车路上坏了怎么办 爱跟别人聊朋友的事怎么办 任职履历上学历写错了怎么办 六个月的宝宝老长婴儿湿疹怎么办 入伍前驾照没考完退伍后过期怎么办 在电脑上玩英雄联盟没有声音怎么办 梦幻西游新区抢不到副本积分怎么办 倒车时遇上机动车碰瓷的怎么办 电瓶车相撞对方全责但不赔偿怎么办 轻微刮蹭逃逸对方想多要钱怎么办 正常开车撞伤了闯红灯的人怎么办 发现小事故要保持现场堵车怎么办 私处刮毛外面皮肤不小心弄伤怎么办 老婆骂孩子不准老公带饿小孩怎么办 结婚3年妻子不让丈夫碰怎么办 老婆出轨现在没证据他要离婚怎么办 结婚后老婆不让碰分房睡离婚怎么办 电动车调速把手变速挡坏了怎么办 路边车辆贴条了超过15天了怎么办 违停交警拍照了没贴条 照片怎么办 支付宝违章缴费罚单输错了怎么办 交警查酒驾跑了把警车撞了怎么办 禁止进入待行区的时候进入了怎么办 今天开车把72岁老太婆撞了怎么办 在这种路口遇到行人突然横穿怎么办 在左拐车道却直行了怎么办 路边简易房让拆除不想拆怎么办 英国护照的名和姓印颠倒了怎么办 加热圈功率小加不到设定温度怎么办 本田飞度05年车尾气不好怎么办