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

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

秒杀多线程第五篇经典线程同步关键段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,所以可以仿照上一篇的实现来写出代码:

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

运行结果如下图:

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

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

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

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

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

第一个程序:

  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. }

第二个程序:

  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. case WAIT_OBJECT_0:
  20. printf("已经收到信号\n");
  21. break;
  22. case WAIT_TIMEOUT:
  23. printf("信号未在规定的时间内送到\n");
  24. break;
  25. }
  26. CloseHandle(hMutex);
  27. return 0;
  28. }

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

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

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

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

最后总结下互斥量Mutex:

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

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

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

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

经典线程同步 互斥量Mutex的更多相关文章

  1. (转)经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  2. 多线程面试题系列(7):经典线程同步 互斥量Mutex

    前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用互斥量Mutex来解决这个问题. 互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

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

    本文转载于:http://blog.csdn.net/morewindows/article/details/7470936 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用 ...

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

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  5. 秒杀多线程第七篇 经典线程同步 互斥量Mutex(续)

    java使用Synchronized关键字实现互斥,而同时有Lock支持. 这两个的效果是等同的,Synchronized性能的起伏较大,而lock比较收敛. 为了代码的可读性,Synchronize ...

  6. pThreads线程(二) 线程同步--互斥量/锁

    互斥量(Mutex)是“mutual exclusion”的缩写.互斥量是实现线程同步,和保护同时写共享数据的主要方法. 互斥量对共享数据的保护就像一把锁.在Pthreads中,任何时候仅有一个线程可 ...

  7. 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  8. 经典线程同步 事件Event

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...

  9. windows多线程同步--互斥量

    关于互斥量的基本概念:百度百科互斥量 推荐参考博客:秒杀多线程第七篇 经典线程同步 互斥量Mutex 注意:互斥量也是一个内核对象,它用来确保一个线程独占一个资源的访问.互斥量与关键段的行为非常相似, ...

随机推荐

  1. 【转载】Linux系统,设置Oracle开机启动,待整理

    http://www.cnblogs.com/mophee/archive/2013/06/03/3115805.html

  2. 转!!JavaBean,List,Map转成json格式

    public class User { private String username; private String password; public String getUsername() { ...

  3. eclipse设置字体、背景(豆绿)色、自动提示

    背景色:(护眼豆绿色) window-->preferences-->General-->Editors-->Text Editors-->(最下遍一栏中的)Backgr ...

  4. --tags --follow-tags 的区别

    --tags    All refs under refs/tags are pushed, in addition to refspecs explicitly listed on the comm ...

  5. phalcon: 视图集成(内嵌模板)

    当一个网站把不同的模板分割成:头部,中间区域,底部时,我们就需要在不同的[中间区域]页面引入相同的头部跟底部.引入有2种方法, 一种是:在controller中调用: $this->view-& ...

  6. 小div在大div中垂直居中,以及div在页面垂直居中

    <html> <head> <title>淘宝 2faner</title> <style type="text/css"&g ...

  7. Unity5.1 新的网络引擎UNET(十五) Networking 引用--上

    http://blog.csdn.net/u010019717/article/details/46993697 孙广东  2015.7.21 本节提供了与网络系统一起使用的组件的详细信息. 1.Ne ...

  8. 20145218 《Java程序设计》课程总结

    20145218 <Java程序设计>课程总结 每周读书笔记链接汇总 20145218<Java程序设计>第一周学习总结 20145218<Java程序设计>第二周 ...

  9. [Nginx] 关键概念解读

        1.正向代理服务器VS反向代理服务器 我们知道,万维网的相互访问必须是外部网络间的相互访问,也就是访问的必须是外网IP或者映射为外网IP的域名.诸如192.168.1.11这样的内网IP是无法 ...

  10. ubuntu下安装基本配置

    安装ubuntu更新: sudo apt-get update sudo apt-get upgrade 安装Docky: sudo add-apt-repository ppa:ricotz/doc ...