MFC互斥信号量的问题请教

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MFC互斥信号量的问题请教相关的知识,希望对你有一定的参考价值。

第一个函数:
BOOL CMutexDlg::OnInitDialog(){
GloMutex = ::CreateMutex(NULL, FALSE, "Mutex1"); //创建互斥信号量
ReleaseMutex(GloMutex);
CWinThread *pTSocket = AfxBeginThread(SktThreadFromSer, this, 。。。。)
SetTimer(1,2000,NULL);
SetTimer(2,5000,NULL);

第二个相关函数:
void CMutexDlg::OnTimer(UINT nIDEvent) {
if(nIDEvent == 1)
int i = WaitForSingleObject(GloMutex, 0);
SetDlgItemInt(IDC_STATIC,i);

if(nIDEvent == 2)
ReleaseMutex(GloMutex);

第三个相关函数:
UINT SktThreadFromSer(LPVOID pParm)
while(1)
WaitForSingleObject(GloMutex, INFINITE);
AfxMessageBox("fdjasi");
ReleaseMutex(GloMutex);
Sleep(5500);


说明:定时器1定时2秒,定时器2定时5秒,线程睡眠5.5秒,开始线程弹出消息框,然后释放互斥量,之后睡眠5.5秒,2秒后定时器1获取信号,4秒时还是获取信号,但此时返回结果应该是258啊,(0x102),但事实还是0,这是错误一,当5秒时释放信号信号,当5.5秒的时候线程醒来,应该会得到信号弹出消息框啊,事实上没有,这是错误二,请教原因!

参考技术A 只需要改一个地方,将:

if(nIDEvent == 2)
ReleaseMutex(GloMutex);
改为
if(nIDEvent == 2)
while(ReleaseMutex(GloMutex));追问

哦,请问这是为什么呢?单纯的调用一下释放不了还是我的程序有漏洞呢?

追答

因为你的1号定时器执行频率较2号高,所以WaitForSingleObject被调用了多次,所以需要多个ReleaseMutex对应释放。

追问

哦?这样啊!看这样子还是对其机制不明白,我还想问一下,昨天你说事件和互斥不等价,实现线程同步的时候,两者有本质的区别吗?我怎么反而感觉事件很好用啊,当事件调用wai后,如果不Set,那么再次调用就会返回没有信号(0x102),可是互斥不一样,就如您所说,重复调用还要重复释放,必须次数对等,而且调用wait时,只要不被别的线程占用,就会执行wait后面的程序,在我程序中感觉事件要比互斥好用,请指教

追答

从名字就知道两个是用于不同目的的,互斥的目的在于“斥”,事件着重于通知。

本回答被提问者采纳

多线程面试题系列(15):关键段,事件,互斥量,信号量的“遗弃”问题

一.什么是“遗弃”问题

在第七篇讲到了互斥量能处理“遗弃”问题,下面引用原文:

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

可见“遗弃”问题就是——占有某种资源的进程意外终止后,其它等待该资源的进程能否感知。

 

二.关键段的“遗弃”问题

关键段在这个问题上很简单——由于关键段不能跨进程使用,所以关键段不需要处理“遗弃”问题。

 

三.事件,互斥量,信号量的“遗弃”问题

事件,互斥量,信号量都是内核对象,可以跨进程使用。一个进程在创建一个命名的事件后,其它进程可以调用OpenEvent()并传入事件的名称来获得这个事件的句柄。因此事件,互斥量和信号量都会遇到“遗弃”问题。我们已经知道互斥量能够处理“遗弃”问题,接下来就来看看事件和信号量是否能够处理“遗弃”问题。类似于第七篇对互斥量所做的试验,下面也对事件和信号量作同样的试验:

1. 创建二个进程。

2. 进程一创建一个初始为未触发的事件,然后等待按键,按下y则触发事件后结束进程,否则直接退出表示进程一已意外终止。

3. 进程二先获得事件的句柄,然后调用WaitForSingleObject()等待这个事件10秒,在这10秒内如果事件已经触发则输出“已收到信号”,否则输出“未在规定的时间内收到信号”。如果在等待的过程中进程一意外终止,则输出“拥有事件的进程意外终止”。信号量的试验方法类似。

为了加强对比效果,将互斥量的试验结果先展示出来(代码请参见第七篇)

技术分享

可以看出在第一个进程在没有触发互斥量就直接退出的情况下,等待这个互斥量的第二个进程是能够感知进程一所发生的意外终止的。

接下来就先完成事件的“遗弃”问题试验代码。

进程一:

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <conio.h>  
  3. #include <windows.h>  
  4. const TCHAR STR_EVENT_NAME[] = TEXT("Event_MoreWindows");  
  5. int main()  
  6. {  
  7.     printf("     经典线程同步 事件的遗弃处理  进程一\n");    
  8.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  9.     HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, STR_EVENT_NAME);//自动置位 当前未触发  
  10.     printf("事件已经创建,现在按y触发事件,按其它键终止进程\n");  
  11.     char ch;  
  12.     scanf("%c", &ch);  
  13.     if (ch != ‘y‘)  
  14.         exit(0); //表示进程意外终止  
  15.     SetEvent(hEvent);  
  16.     printf("事件已经触发\n");  
  17.     CloseHandle(hEvent);  
  18.     return 0;  
  19. }  

进程二:

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. const TCHAR STR_EVENT_NAME[] = TEXT("Event_MoreWindows");  
  4. int main()  
  5. {  
  6.     printf("     经典线程同步 事件的遗弃处理  进程二\n");    
  7.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  8.   
  9.     HANDLE hEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, STR_EVENT_NAME); //打开事件  
  10.     if (hEvent == NULL)  
  11.     {  
  12.         printf("打开事件失败\n");  
  13.         return 0;  
  14.     }  
  15.     printf(" 等待中....\n");  
  16.     DWORD dwResult = WaitForSingleObject(hEvent, 10 * 1000); //等待事件被触发  
  17.     switch (dwResult)  
  18.     {  
  19.     case WAIT_ABANDONED:  
  20.         printf("拥有事件的进程意外终止\n");  
  21.         break;  
  22.   
  23.     case WAIT_OBJECT_0:  
  24.         printf("已经收到信号\n");  
  25.         break;  
  26.   
  27.     case WAIT_TIMEOUT:  
  28.         printf("未在规定的时间内收到信号\n");  
  29.         break;  
  30.     }  
  31.     CloseHandle(hEvent);  
  32.     return 0;  
  33. }  

事件Event试验结果1-进程一触发事件后正常结束:

技术分享

事件Event试验结果2-进程一意外终止:

技术分享

可以看出进程二没能感知进程一意外终止,说明事件不能处理“遗弃”问题。

 

下面再来试下信号量。

信号量的“遗弃”问题试验代码:

进程一:

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <conio.h>  
  3. #include <windows.h>  
  4. const TCHAR STR_SEMAPHORE_NAME[] = TEXT("Semaphore_MoreWindows");  
  5. int main()  
  6. {  
  7.     printf("     经典线程同步 信号量的遗弃处理  进程一\n");    
  8.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  9.   
  10.     HANDLE hSemaphore = CreateSemaphore(NULL, 0, 1, STR_SEMAPHORE_NAME);//当前0个资源,最大允许1个同时访问  
  11.     printf("信号量已经创建,现在按y触发信号量,按其它键终止进程\n");  
  12.     char ch;  
  13.     scanf("%c", &ch);  
  14.     if (ch != ‘y‘)  
  15.         exit(0); //表示进程意外终止  
  16.     ReleaseSemaphore(hSemaphore, 1, NULL);  
  17.     printf("信号量已经触发\n");  
  18.     CloseHandle(hSemaphore);  
  19.     return 0;  
  20. }  

进程二:

[cpp] view plain copy
 
  1. #include <stdio.h>  
  2. #include <windows.h>  
  3. const TCHAR STR_SEMAPHORE_NAME[] = TEXT("Semaphore_MoreWindows");  
  4. int main()  
  5. {  
  6.     printf("     经典线程同步 信号量的遗弃处理  进程二\n");    
  7.     printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  8.   
  9.     HANDLE hSemaphore = OpenSemaphore (SEMAPHORE_ALL_ACCESS, TRUE, STR_SEMAPHORE_NAME); //打开信号量  
  10.     if (hSemaphore == NULL)  
  11.     {  
  12.         printf("打开信号量失败\n");  
  13.         return 0;  
  14.     }  
  15.     printf(" 等待中....\n");  
  16.     DWORD dwResult = WaitForSingleObject(hSemaphore, 10 * 1000); //等待信号量被触发  
  17.     switch (dwResult)  
  18.     {  
  19.     case WAIT_ABANDONED:  
  20.         printf("拥有信号量的进程意外终止\n");  
  21.         break;  
  22.   
  23.     case WAIT_OBJECT_0:  
  24.         printf("已经收到信号\n");  
  25.         break;  
  26.   
  27.     case WAIT_TIMEOUT:  
  28.         printf("未在规定的时间内收到信号\n");  
  29.         break;  
  30.     }  
  31.     CloseHandle(hSemaphore);  
  32.     return 0;  
  33. }  

信号量Semaphore试验结果1-进程一触发信号量后正常结束

技术分享

信号量Semaphore试验结果2-进程一意外终止

技术分享

可以看出进程二没能感知进程一意外终止,说明信号量与事件一样都不能处理“遗弃”问题。

 

四.“遗弃”问题总结

由本文所做的试验可知,互斥量能够处理“遗弃”情况,事件与信号量都无法解决这一情况。

再思考下互斥量能处理“遗弃”问题的原因,其实正是因为它有“线程所有权”概念。在系统中一旦有线程结束后,系统会判断是否有互斥量被这个线程占有,如果有,系统会将这互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这表示该互斥量已经不为任何线程占用,处于触发状态。其它等待这个互斥量的线程就能顺利执行下去了。至于线程如何获取互斥量的“线程所有权”,MSDN上介绍为——A thread obtainsownership of a mutex either by creating it with the bInitialOwnerparameter set to TRUE or by specifying its handle in a call toone of the wait functions.

 

以上是关于MFC互斥信号量的问题请教的主要内容,如果未能解决你的问题,请参考以下文章

信号量与进程/线程间同步与互斥

多线程面试题系列(15):关键段,事件,互斥量,信号量的“遗弃”问题

pthreads互斥与信号量

简述利用信号量实现两个进程互斥的实现

锁、互斥量和信号量有啥区别?

MFC ResumeThread 和 std::condition_variable 等待后偶尔线程同步失败