了解WaitForSingleObject中WAIT_ABANDONED 返回值
1、互斥量内核对象
互斥量内核对象用来确保一个线程独占对一个资源的访问。互斥量对象包含一个使用计数、线程ID以及递归计数。互斥量与关键段的行为完全相同。但是互斥量是内核对象,而关键段是用户模式下的同步对象。这意味着互斥量比关键段慢。但这同时意味着不同进程中的线程可以访问同一互斥量,还意味着线程可以在等待对资源的访问权的同时指定一个最长等待时间。
线程ID用来标识当前占用这个互斥量的是系统中的哪个线程,递归计数表示这个线程占用该互斥量的次数。互斥量一般用来对多个线程访问同一块内存进行保护。它可以确保正在访问的内存块的任何线程会独占内存块的访问权。
互斥量的规则:
- 如果线程ID为0(无效线程ID),那么该互斥量不为任何线程所占用,它处于触发状态。
- 如果线程ID为非零值,那么有一个线程已经占用了该信号量,它处于未触发状态。
- 于所有其他内核对象不同,操作系统对互斥量进行了特殊处理,允许它们违反一些常规规则。
要使用互斥量,进程必须先调用CreateMutex来创建一个互斥量
HANDLE CreateMutex(
__in_opt LPSECURITY_ATTRIBUTES lpMutexAttributes,
__in BOOL bInitialOwner,
__in_opt LPCSTR lpName
);
参数bInitialOwner用来控制互斥量的初始状态。如果传的是FALSE(通常情况) 那么互斥量对象的线程ID和递归计数都被设为0。这意味着互斥量不为任何线程所占用,因此处于触发状态。
如果该参数为TRUE,那么对象的线程ID被设定为调用线程的线程ID,递归计数将被设定为1。由于线程ID为非零值,因此互斥量最初处于未触发状态。
下面就正常情况给出一个例子说明互斥量的常规使用方法:(例子在VS2010下编译运行)
首先编写main函数
#include <stdio.h>
#include <Windows.h>
#include <process.h> int main(int argc, char* argv[])
{
HANDLE hMutex = NULL;
hMutex = CreateMutexA(NULL,FALSE,"test1234qwer");
HANDLE hThread1 = CreateThread(NULL, , LisDevProc1, (LPVOID)&hMutex, , NULL);
HANDLE hThread2 = CreateThread(NULL, , LisDevProc2, (LPVOID)&hMutex, , NULL); Sleep(); if(hThread1)
CloseHandle( hThread1 ); if(hThread2)
CloseHandle( hThread2 ); CloseHandle(hMutex); system("pause");
return ;
}
该函数创建了1个互斥量及两个线程,然后等待10s释放相关资源结束。
DWORD WINAPI LisDevProc1(LPVOID para)
{
HANDLE* phMutex = (HANDLE*)para; WaitForSingleObject(*phMutex,INFINITE); printf("Enter Thread1\n");
printf("I'm sleeping……\n"); Sleep(); printf("Leave Thread1\n"); ReleaseMutex(*phMutex);
return ;
}
上述是线程1的处理函数,先等待该互斥量然后sleep3秒然后释放该互斥量。
线程2处理函数跟线程1相同,如下
DWORD WINAPI LisDevProc2(LPVOID para)
{
HANDLE* phMutex = (HANDLE*)para; WaitForSingleObject(*phMutex,INFINITE); printf("Enter Thread2\n");
printf("I'm sleeping……\n"); Sleep(); printf("Leave Thread2\n"); ReleaseMutex(*phMutex);
return ;
}
执行结果:

执行顺序是:线程1先通过WaitForSingleObject获取互斥量的所有权,打印Enter,然后等待3秒,打印Leave,最后释放互斥量的所有权,然后线程2才获取到互斥量的所有权。。。可以看到,互斥量确实实现了对共享资源的保护。
上边还提到了一条特殊的规则,
线程在试图等待一个未触发的互斥量对象时,通常线程会进入等待状态。但是,系统会检查想要获得互斥量的线程的线程ID于互斥量内部记录的线程ID是否相同,如果线程ID一致,那么系统会让线程保持可调度状态——即使该互斥量尚未触发。每次线程成功的等待了一个互斥量,互斥量的递归计数会递增。使递归计数大于1的唯一途径就是利用这一例外,让线程多次等待同一互斥量。
对上述特例,先把以上代码中线程1的代码改为如下:
DWORD WINAPI LisDevProc1(LPVOID para)
{
HANDLE* phMutex = (HANDLE*)para; WaitForSingleObject(*phMutex,INFINITE);
WaitForSingleObject(*phMutex,INFINITE);
printf("Enter Thread1\n");
printf("I'm sleeping……\n"); Sleep(); printf("Leave Thread1\n");
ReleaseMutex(*phMutex);
ReleaseMutex(*phMutex);
return ;
}
可以看到,线程1的处理函数调用了两次WaitForSingleObject,结果跟之前结果一样,印证了该特例确实存在。
以上说来了这么多,才刚刚进入主题,讨论一下遗弃问题。
互斥量的这种线程所有权的概念导致出现遗弃问题。
如果占用互斥量的线程在释放互斥量之前终止(使用ExitThread,TerminateThread,ExitProcess,TerminateProcess)那么对于互斥量和正在等待该互斥量的线程来说会发生什么情况?答案是系统会认为互斥量被遗弃(abandoned),因为占用它的线程已经终止,因此无法释放它。
因为系统会记录所有的互斥量和线程内核对象,因此它确切的知道互斥量何时被遗弃。当互斥量被遗弃的时候,系统会自动将互斥量的线程ID设为0,将它的递归计数设为0。然后系统检查有没有其他线程正在等待该互斥量。如果有,那么系统会公平的选择一个正在等待的线程,把对象内部的线程Id设为所选择的那个线程的线程ID,并将递归计数设为1,这样被选择的线程就变成可调度状态了。
一旦检测到某互斥量被检测到,则WaitForSingleObject返回的不是WAIT_OBJECT_0,而是一个特殊值WAIT_ABANDONED。
返回该值,说明等待的互斥量被某个线程遗弃,同时说明被保护的资源已经被破坏了。这种情况下,写的程序自己必须决定该怎么做。
看下一下程序代码:
#include <stdio.h>
#include <Windows.h>
#include <process.h> DWORD WINAPI LisDevProc1(LPVOID para)
{
HANDLE* phMutex = (HANDLE*)para; WaitForSingleObject(*phMutex,INFINITE);
printf("Enter Thread1\n");
printf("I'm sleeping……\n"); Sleep(); printf("Leave Thread1\n");
ReleaseMutex(*phMutex);
return ;
} DWORD WINAPI LisDevProc2(LPVOID para)
{ HANDLE* phMutex = (HANDLE*)para;
int ret;
int flag;
do{
flag = ;
ret = WaitForSingleObject(*phMutex,INFINITE);
switch(ret)
{
case WAIT_OBJECT_0:
printf("normal ....\n");
break;
case WAIT_ABANDONED:
flag = ;
printf("abandoned ....\n");
break;
}
}while(flag);
printf("Enter Thread2\n");
printf("I'm sleeping……\n"); Sleep(); printf("Leave Thread2\n");
ReleaseMutex(*phMutex);
return ;
} int main(int argc, char* argv[])
{
HANDLE hMutex = NULL;
hMutex = CreateMutexA(NULL,FALSE,"test1234qwer");
HANDLE hThread1 = CreateThread(NULL, , LisDevProc1, (LPVOID)&hMutex, , NULL);
HANDLE hThread2 = CreateThread(NULL, , LisDevProc2, (LPVOID)&hMutex, , NULL); Sleep();
TerminateThread(hThread1, ); Sleep(); if(hThread1)
CloseHandle( hThread1 ); if(hThread2)
CloseHandle( hThread2 ); CloseHandle(hMutex); system("pause");
return ;
}
在main函数中,创建了2个线程后的1.5秒 执行了一句TerminateThread结束了线程1,线程1没来得及释放互斥量就挂掉了,(慎重用TerminateThread等函数)看下结果如下:

可以看到在杀死线程1后,线程2的WaitForSingleObject立刻返回WAIT_ABANDONED,然后线程2再次WaitForSingleObject时又立刻返回WAIT_OBJECT_0
最后,一定注意遗弃问题的产生,如果产生,说明受保护的共享数据可能已经被破坏掉了。
了解WaitForSingleObject中WAIT_ABANDONED 返回值的更多相关文章
- Asp.net MVC 中Controller返回值类型ActionResult
[Asp.net MVC中Controller返回值类型] 在mvc中所有的controller类都必须使用"Controller"后缀来命名并且对Action也有一定的要求: 必 ...
- [改善Java代码]不要在finally块中处理返回值
在finally代码块中处理返回值,这是在面试题中经常出现的题目.但是在项目中绝对不能再finally代码块中出现return语句,这是因为这种处理方式非常容易产生"误解",会严重 ...
- Controller 中Action 返回值类型 及其 页面跳转的用法
•Controller 中Action 返回值类型 View – 返回 ViewResult,相当于返回一个View 页面. -------------------------------- ...
- robot framework中的返回值
1.若想要再setup中有返回值,给后续的操作使用 A)在setup的关键词中需要的返回值,设置为global variable或者suit variable:如下图:但是在编译器中,会报错,但是执行 ...
- Web API中的返回值类型
WebApi中的返回值类型大致可分为四种: Void/ IHttpActionResult/ HttpResponseMessage /自定义类型 一.Void void申明方法没有返回值,执行成功后 ...
- c++中带返回值函数没写return能通过编译但运行时会出现奇怪问题
c++中带返回值函数没写return能通过编译但运行时会出现奇怪问题 例如: string myFunc(){ theLogics(); } 发现调用: myFunc(); 崩溃. 但调用: cout ...
- try--catch--finally中return返回值执行的顺序(区别)
1.try块中没有抛出异常,try.catch和finally块中都有return语句 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static int ...
- C++ 中的返回值
C++中大致有三种返回值:值拷贝(副本),值引用和指针,返回什么类型的值要根据当时情况而定. 如果返回的是大型对象的副本,那么在每一次的函数调用后返回,都会调用该对象类型的拷贝构造函数构造一个新的副本 ...
- javascript学习笔记-2:jQuery中$("xx")返回值探究
最近在写一个jQuery插件的时候,需要用到一个条件: 一组img标签,每一个元素都需要被它前面的元素值src替换,如果是第一个(序列为0)则其值为最后一个元素值,如果是最后一个,那么其值为第一个元素 ...
随机推荐
- Jenkins持续集成企业实战系列之两种网站部署的流程-----01
注:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任. 最初接触Jenkins也是由于公司需求,根据公司需求Java代码项目升级的.(公司是 ...
- SDUT OJ 数组计算机(线段树)
学长推荐了这个博客详细的介绍了线段树的建立.查找.更新: 数组计算机 Time Limit: 1000 ms Memory Limit: 65536 KiB Submit Statistic Prob ...
- pycharm中设置工程
1.设置pycharm多工程以及工程的依赖关系: a.在打开工程时候,选中add current open projects(添加到项目中), b.设置(settings)->project ...
- 小程序tabBar显示问题
我一直在纠结为什么小程序在有的页面显示,有的页面不显示 查了一下才知道,小程序tabBar只在tabBar中的list定义了页面的才会显示,其余页面不会显示 如下例:tabBar只在画红框的页面显示
- CentOS 6.* 安装node
CentOS6 安装node 使用node -v 是报错 ./node: error while loading shared libraries: libstdc++.so.6: cannot op ...
- P3704 [SDOI2017]数字表格 (莫比乌斯反演)
[题目链接] https://www.luogu.org/problemnew/show/P3704 [题解] https://www.luogu.org/blog/cjyyb/solution-p3 ...
- Avito Cool Challenge 2018:D. Maximum Distance (最小生成树)
题目链接 题意 : 给出一个联通图和一些特殊的点,现在定义cost(u,v)为一条从u到v的路径上面边权的最大值 , 定义dis(u,v) 为从u到v 路径上面cost 的最小值 然后求所有特殊点到其 ...
- phantomjs的和谷歌浏览器的简单使用
一.phantomjs的简单使用 ''' 什么是phantomJs:无界面的浏览器 ''' from selenium import webdriver from time import sleep ...
- 【研究】Weblogic XMLDecoder反序列化漏洞(CVE-2017-10271)
影响范围: Oracle WebLogic Server 10.3.6.0.0版本 Oracle WebLogic Server 12.1.3.0.0版本 Oracle WebLogic Server ...
- linux下的常见信号总结
在linux下有很多信号,按可靠性分为可靠信号和非可靠信号,按时间分为实时信号和非实时信号,linux进程也有三种方式来处理收到的信号: (1)忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略 ...