了解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)则其值为最后一个元素值,如果是最后一个,那么其值为第一个元素 ...
随机推荐
- 【转】在Win10家庭版中启用组策略
源地址:https://www.baidu.com/link?url=tZrD7LVxQEKQUTWUum86LoxyaxWNLs5BeBE2K36TliRi8sjGraKc-iP3TEm6sc_KX ...
- 洛谷P2287 [HNOI2004]最佳包裹(三维凸包)
题面 传送门 题解 左转板子,调个精度就能\(A\)了 //minamoto #include<bits/stdc++.h> #define R register #define fp(i ...
- mongoengine使用示例
#!/usr/bin/env python # -*- coding: utf-8 -*- ''' Created on 2017年10月19日 @author: zzy ''' from mongo ...
- 50个php程序性能优化的方法,赶紧收藏吧!
1. 用单引号代替双引号来包含字符串,这样做会更快一些.因为 PHP 会在双引号包围的 字符串中搜寻变量,单引号则不会,注意:只有 echo 能这么做,它是一种可以把多个字符 串当作参数的“函数”(译 ...
- shell-003:用for循环统计内存使用量
shell-100主要是用于练习! #!/bin/bash # 统计内存的使用量(这里用ps统计) # 第一步:不打印第一行,这里的sed ‘1d’ 去掉 for n in `ps aux |sed ...
- Qt 学习之路 2(50):自定义可编辑模型
Home / Qt 学习之路 2 / Qt 学习之路 2(50):自定义可编辑模型 Qt 学习之路 2(50):自定义可编辑模型 豆子 2013年5月13日 Qt 学习之路 2 13条评论 上一章我们 ...
- [PowerShell]Quote in String
今天遇到一个问题,如何在Select-String的Pattern参数里能使用双引号 比如 Select-String -path . -pattern "Lines: <span c ...
- 数据库,asp总结思维导图图片
- 2018acm-icpc青岛站心得
今年总共两场区域赛,一场南京,一场青岛.南京场队伍真正开始磨合,虽然最后还是铜牌,但是和银牌队伍其实只差一个计算几何的板子的问题.而鉴于南京的教训,所以在准备青岛站的时候,我准备了非常多的模板,还和派 ...
- [转] Nook Glowlight Plus入门指南(Root及相关软件设置)
[From] http://www.dakang.info/nook-glowlight-plus-root/ 本文仅仅是对大神 xukong及众多热心Hper帖子的一个汇总,稍作个人补充,方便查询, ...