一.什么是“遗弃”问题
互斥量常用于多进程之间的线程互斥,所以它比关键段还多一个很有用的特性——“遗弃”情况的处理。比如有一个占用互斥量的线程在调用ReleaseMutex()触发互斥量前就意外终止了(相当于该互斥量被“遗弃”了),那么所有等待这个互斥量的线程是否会由于该互斥量无法被触发而陷入一个无穷的等待过程中了?这显然不合理。因为占用某个互斥量的线程既然终止了那足以证明它不再使用被该互斥量保护的资源,所以这些资源完全并且应当被其它线程来使用。因此在这种“遗弃”情况下,系统自动把该互斥量内部的线程ID设置为0,并将它的递归计数器复置为0,表示这个互斥量被触发了。然后系统将“公平地”选定一个等待线程来完成调度(被选中的线程的WaitForSingleObject()会返回WAIT_ABANDONED_0)。
可见“遗弃”问题就是——占有某种资源的进程意外终止后,其它等待该资源的进程能否感知。
二.关键段的“遗弃”问题
关键段在这个问题上很简单——由于关键段不能跨进程使用,所以关键段不需要处理“遗弃”问题。
三.事件,互斥量,信号量的“遗弃”问题
事件,互斥量,信号量都是内核对象,可以跨进程使用。一个进程在创建一个命名的事件后,其它进程可以调用OpenEvent()并传入事件的名称来获得这个事件的句柄。因此事件,互斥量和信号量都会遇到“遗弃”问题。
1. 创建二个进程。
2. 进程一创建一个初始为未触发的事件,然后等待按键,按下y则触发事件后结束进程,否则直接退出表示进程一已意外终止。
3. 进程二先获得事件的句柄,然后调用WaitForSingleObject()等待这个事件10秒,在这10秒内如果事件已经触发则输出“已收到信号”,否则输出“未在规定的时间内收到信号”。如果在等待的过程中进程一意外终止,则输出“拥有事件的进程意外终止”。信号量的试验方法类似。
进程1的是实现(CreateKernelObj)
#include <stdio.h>
#include <conio.h>
#include <windows.h>
#include<iostream>
#include<iomanip>
using namespace std;
#define OBJTYPE 3
#define OBJEVENT 1
#define OBJMUTEX 2
#define OBJSEMAPHORE 3
void CreateKernelObj()
{
cout << "遗弃内核对象的进程【Pid : " << GetCurrentProcessId() << "】" << endl;
HANDLE hCreate = NULL;
#if OBJTYPE == OBJEVENT
hCreate = CreateEvent(NULL, FALSE, FALSE, TEXT("FindMeCatchMe"));//创建事件
#elif OBJTYPE == OBJMUTEX
hCreate = CreateMutex(NULL, TRUE, TEXT("FindMeCatchMe"));//创建互斥量
#elif OBJTYPE == OBJSEMAPHORE
hCreate = CreateSemaphore(NULL, 0, 1, TEXT("FindMeCatchMe"));//创建信号量
#endif
if(hCreate == NULL) return;
cout << "未触发内核对象已经创建,是否退出进程(Yes/No) : ";
char cChoice; cin >> cChoice;
if(cChoice == 'y' || cChoice == 'Y') return;
#if OBJTYPE == OBJEVENT
SetEvent(hCreate);//触发事件
#elif OBJTYPE == OBJMUTEX
ReleaseMutex(hCreate);//触发互斥量
#elif OBJTYPE == OBJSEMAPHORE
ReleaseSemaphore(hCreate, 1, NULL);//触发互斥量
#endif
cout << "内核对象已经被触发!" << endl;
CloseHandle(hCreate);
return;
}
进程2的是实现(OpenKernelObj)
#include <stdio.h>
#include <windows.h>
#include<iostream>
#include<iomanip>
using namespace std;
#define OBJTYPE 3
#define OBJEVENT 1
#define OBJMUTEX 2
#define OBJSEMAPHORE 3
//可以看出进程二没能感知进程一意外终止,说明事件不能处理“遗弃”问题
void OpenKernelObj()
{
cout << "等待内核对象的进程【Pid : " << GetCurrentProcessId() << "】" << endl;
HANDLE hOpen = NULL;
#if OBJTYPE == OBJEVENT
hOpen = OpenEventW(EVENT_ALL_ACCESS, TRUE, TEXT("FindMeCatchMe"));//打开事件
#elif OBJTYPE == OBJMUTEX
hOpen = OpenMutex(MUTEX_ALL_ACCESS, TRUE, TEXT("FindMeCatchMe"));//打开互斥量
#elif OBJTYPE == OBJSEMAPHORE
hOpen = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, TEXT("FindMeCatchMe"));//打开信号量
#endif
if(hOpen == NULL){
cout << "内核对象打开失败:内核对象可能尚未创建!" << endl;
return;
}
cout << "等待中······" << endl;
int iCnt = 0;
while(TRUE){
DWORD dwRet = WaitForSingleObject(hOpen, 10*1000);
cout << "等待时长:" << (++iCnt)*10 << "秒······" << endl;
if(dwRet == WAIT_ABANDONED){
cout << "创建内核对象的进程意外终止!" << endl;
break;
}
else if(dwRet == WAIT_OBJECT_0){
cout << "等待到内核对象被触发!" << endl;
break;
}
else if(dwRet == WAIT_TIMEOUT){
if(iCnt >= 6){//如果等了1分钟,内核对象还未被触发,程序退出!
cout << "等待超时,程序自动退出!" << endl;
break;
}
}
}
CloseHandle(hOpen);
return;
}