多线程面试题系列(7):经典线程同步 互斥量Mutex
前面介绍了关键段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,所以可以仿照上一篇的实现来写出代码:
- //经典线程同步问题 互斥量Mutex
- #include <stdio.h>
- #include <process.h>
- #include <windows.h>
- long g_nNum;
- unsigned int __stdcall Fun(void *pPM);
- const int THREAD_NUM = 10;
- //互斥量与关键段
- HANDLE g_hThreadParameter;
- CRITICAL_SECTION g_csThreadCode;
- int main()
- {
- printf(" 经典线程同步 互斥量Mutex\n");
- printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
- //初始化互斥量与关键段 第二个参数为TRUE表示互斥量为创建线程所有
- g_hThreadParameter = CreateMutex(NULL, FALSE, NULL);
- InitializeCriticalSection(&g_csThreadCode);
- HANDLE handle[THREAD_NUM];
- g_nNum = 0;
- int i = 0;
- while (i < THREAD_NUM)
- {
- handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);
- WaitForSingleObject(g_hThreadParameter, INFINITE); //等待互斥量被触发
- i++;
- }
- WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);
- //销毁互斥量和关键段
- CloseHandle(g_hThreadParameter);
- DeleteCriticalSection(&g_csThreadCode);
- for (i = 0; i < THREAD_NUM; i++)
- CloseHandle(handle[i]);
- return 0;
- }
- unsigned int __stdcall Fun(void *pPM)
- {
- int nThreadNum = *(int *)pPM;
- ReleaseMutex(g_hThreadParameter);//触发互斥量
- Sleep(50);//some work should to do
- EnterCriticalSection(&g_csThreadCode);
- g_nNum++;
- Sleep(0);//some work should to do
- printf("线程编号为%d 全局资源值为%d\n", nThreadNum, g_nNum);
- LeaveCriticalSection(&g_csThreadCode);
- return 0;
- }
运行结果如下图:
可以看出,与关键段类似,互斥量也是不能解决线程间的同步问题。
联想到关键段会记录线程ID即有“线程拥有权”的,而互斥量也记录线程ID,莫非它也有“线程拥有权”这一说法。
答案确实如此,互斥量也是有“线程拥有权”概念的。“线程拥有权”在关键段中有详细的说明,这里就不再赘述了。另外由于互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用ReleaseMutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程ID设置为0,并将它的递归计数器复置为0,表示这个互斥量被触发了。然后系统将“公平地”选定一个等待线程来完成调度(被选中的线程的WaitForSingleObject()会返回WAIT_ABANDONED_0)。
下面写二个程序来验证下:
第一个程序创建互斥量并等待用户输入后就触发互斥量。第二个程序先打开互斥量,成功后就等待并根据等待结果作相应的输出。详见代码:
第一个程序:
- #include <stdio.h>
- #include <conio.h>
- #include <windows.h>
- const char MUTEX_NAME[] = "Mutex_MoreWindows";
- int main()
- {
- HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME); //创建互斥量
- printf("互斥量已经创建,现在按任意键触发互斥量\n");
- getch();
- //exit(0);
- ReleaseMutex(hMutex);
- printf("互斥量已经触发\n");
- CloseHandle(hMutex);
- return 0;
- }
第二个程序:
- #include <stdio.h>
- #include <windows.h>
- const char MUTEX_NAME[] = "Mutex_MoreWindows";
- int main()
- {
- HANDLE hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, MUTEX_NAME); //打开互斥量
- if (hMutex == NULL)
- {
- printf("打开互斥量失败\n");
- return 0;
- }
- printf("等待中....\n");
- DWORD dwResult = WaitForSingleObject(hMutex, 20 * 1000); //等待互斥量被触发
- switch (dwResult)
- {
- case WAIT_ABANDONED:
- printf("拥有互斥量的进程意外终止\n");
- break;
- case WAIT_OBJECT_0:
- printf("已经收到信号\n");
- break;
- case WAIT_TIMEOUT:
- printf("信号未在规定的时间内送到\n");
- break;
- }
- CloseHandle(hMutex);
- return 0;
- }
运用这二个程序时要先启动程序一再启动程序二。下面展示部分输出结果:
结果一.二个进程顺利执行完毕:
结果二.将程序一中//exit(0);前面的注释符号去掉,这样程序一在触发互斥量之前就会因为执行exit(0);语句而且退出,程序二会收到WAIT_ABANDONED消息并输出“拥有互斥量的进程意外终止”:
有这个对“遗弃”问题的处理,在多进程中的线程同步也可以放心的使用互斥量。
最后总结下互斥量Mutex:
1.互斥量是内核对象,它与关键段都有“线程所有权”所以不能用于线程的同步。
2.互斥量能够用于多个进程之间线程互斥问题,并且能完美的解决某进程意外终止所造成的“遗弃”问题。
下一篇将介绍使用信号量Semaphore来解决这个经典线程同步问题。
多线程面试题系列(7):经典线程同步 互斥量Mutex的更多相关文章
- 秒杀多线程第七篇 经典线程同步 互斥量Mutex
本文转载于:http://blog.csdn.net/morewindows/article/details/7470936 前面介绍了关键段CS.事件Event在经典线程同步问题中的使用.本篇介绍用 ...
- 转--- 秒杀多线程第七篇 经典线程同步 互斥量Mutex
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- 经典线程同步 互斥量Mutex
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- (转)经典线程同步 互斥量Mutex
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- 秒杀多线程第七篇 经典线程同步 互斥量Mutex(续)
java使用Synchronized关键字实现互斥,而同时有Lock支持. 这两个的效果是等同的,Synchronized性能的起伏较大,而lock比较收敛. 为了代码的可读性,Synchronize ...
- pThreads线程(二) 线程同步--互斥量/锁
互斥量(Mutex)是“mutual exclusion”的缩写.互斥量是实现线程同步,和保护同时写共享数据的主要方法. 互斥量对共享数据的保护就像一把锁.在Pthreads中,任何时候仅有一个线程可 ...
- 转---秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- 秒杀多线程第六篇 经典线程同步 事件Event
原文地址:http://blog.csdn.net/morewindows/article/details/7445233 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权” ...
- 转--- 秒杀多线程第六篇 经典线程同步 事件Event
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇 一个经典的多线程同步问题> <秒杀多线程第五篇 经典线程同步关键段CS> 上一篇中使用关键段来解决经典的多线程同步互斥问题 ...
随机推荐
- Cache替换算法:LRU与LFU的区别
LFU(Least Frequently Used)最近最少使用算法.它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路.LRU(Least Rece ...
- 安装mvn,jdk,rocketmq
附一个rocketmq各语言客户端仓库:https://github.com/apache/incubator-rocketmq-externals,我用go客户端,但是master分支的go没有pr ...
- ThinkPHP5 封装邮件发送服务(可带附件)
1.Composer 安装 phpmailer composer require phpmailer/phpmailer 2.ThinkPHP 中封装邮件服务类 我把它封装在扩展目录 extend/M ...
- Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改
Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改 Win7下C:\Users\Cortana以账户名称命名的系统文件夹用户名的修改 即修改Cmd命令提示符:C:\Use ...
- 7.31.1 java内存的主要划分
java内存的主要划分:class文件加载到方法区,方法区还存放静态变量和常量,方法区开始执行程序,当调用方法时,会将该方法的栈帧压到栈区,该栈帧中存放局部变量,当方法中new出一个对象,则会在堆中开 ...
- 关于xmlHttp.status最新统计
AJAX中请求远端文件.或在检测远端文件是否掉链时,都需要了解到远端服务器反馈的状态以确定文件的存在与否. Web服务器响应浏览器或其他客户程序的请求时,其应答一般由以下几个部分组成:一个状态行,几个 ...
- windows 10 安装tensorflow
人工智能一浪接一浪,随着谷歌公布tensorflow源码,尤其是支持windows 10平台的python3.5以上版本,更是让更多人都想用windows操作tensorflow. 第一次安装,也不知 ...
- pdf.js跨域问题的分析
最近在做公司业务时,需要在线浏览pdf.在现代浏览器中, 均可直接浏览pdf文件,无兼容性问题.但公司的业务线需要支持到ie8. 经过自己的搜索调研, pdf.js比较适合.pdf.js兼容到ie9, ...
- Android studio一些常见技巧(不断更新)
一.Android studio取消默认每次打开时打开最后一个项目 二.as添加jar包 新建一个libs目录,在java下 进行手动gragle同步或者如下图 三.代码边上的小图标 1.布局文件中存 ...
- 面试题收集---grep和find的区别
grep是通过文件找内容 find 是通过内容找文件 Linux系统中grep命令是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来. 而linux下的find, 在目录结构 ...