今天在调试程序中发现了程序中出现的一个问题,具体如下:

在对话框中新建一个线程worker thread,当用户点击cancel时,通知该线程函数退出,同时用WaitForSingleObject等待该线程结束。但是每当用户点击Cancel后,程序会卡在OnCancel函数中的WaitForSingleObject处,必须要强制结束才能退出。

在网上查了一下,大致原因如下:

WaitForSingleObject会阻塞对话框线程(Dialog thread),同时也会导致了对话框的消息循环机制被阻塞 ,而我在线程函数中会对对话框有一些UI操作(SetPos, SetWindowText),这些对对话框的UI操作实际上是通过线程向控件发送消息得到的 ( SendMessage(m_hWnd, PBM_SETPOS, nPos, 0L) ),因此WaitForSingleObject阻塞了消息循环,也就导致 SendMessage无法返回,卡住了线程 ,WaitForSingleObject也就无法返回了。

具体的修改方法如下:

使用 MsgWaitForMultipleObjects代替 WaitForSingleObject, 这个函数即可以等待信号(thread,event,mutex等等),也可以等待指定类型的消息(MSG),函数声明如下:

DWORD WINAPI MsgWaitForMultipleObjects(

__in                         DWORD nCount, //number of objects to be wait

__in                         const HANDLE* pHandles, //An array of object handles.

__in                         BOOL bWaitAll, //If this parameter is TRUE, the function returns when the states

//of all objects in the pHandles array have been set to signaled

//and an message has been received.

__in                         DWORD dwMilliseconds, //time-out interva

__in                         DWORD dwWakeMask //the type of message to be wait

);

参数dwWakeMask定义了要等待的消息的类型,根据这个函数,将WaitForSingleObject改写为:

[cpp] view plaincopy print?
  1. DWORD dwRet = 0;
  2. MSG msg;
  3. while (TRUE)
  4. {
  5. //wait for m_hThread to be over,and wait for
  6. //QS_ALLINPUT(Any message is in the queue)
  7. dwRet = MsgWaitForMultipleObjects (1, &m_hThread,   FALSE, INFINITE, QS_ALLINPUT);
  8. switch(dwRet)
  9. {
  10. case WAIT_OBJECT_0:
  11. break; //break the loop
  12. case WAIT_OBJECT_0 + 1:
  13. //get the message from Queue
  14. //and dispatch it to specific window
  15. PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
  16. DispatchMessage(&msg);
  17. continue;
  18. default:
  19. break; // unexpected failure
  20. }
  21. break;
  22. }

DWORD dwRet = 0; MSG msg; while (TRUE) {//wait for m_hThread to be over,and wait for//QS_ALLINPUT(Any message is in the queue)dwRet = MsgWaitForMultipleObjects (1, &m_hThread, FALSE, INFINITE, QS_ALLINPUT);switch(dwRet){case WAIT_OBJECT_0: break; //break the loopcase WAIT_OBJECT_0 + 1://get the message from Queue//and dispatch it to specific windowPeekMessage(&msg, NULL, 0, 0, PM_REMOVE);DispatchMessage(&msg); continue;default:break; // unexpected failure}break; }

其中, PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)和DispatchMessage(&msg)这两句不能少,如果缺少程序仍然会卡在UI操作(SetPos, SetWindowText)处,由此判定MsgWaitForMultipleObjects实际上也阻塞了消息循环。那么,PeekMessage和 DispatchMessage是怎样工作,使得消息循环可以正常工作?对此,我有以下两点猜测:

猜测1: PeekMessage和 DispatchMessage将SetPos中发送的 PBM_SETPOS 消息从队列中取出并且分发给相应的窗口,即完成了消息处理。为了验证这个方法,我跟踪了一下PeekMessage得到的msg,发现并没有 PBM_SETPOS 消息。这是为什么呢?这是由于SetPos是调用的SendMessage来更新进度条,因此该消息并不进入对话框线程的Message Queue中,所以PeekMessage 并不能得到PBM_SETPOS消息,也就是说被阻塞的 PBM_SETPOS消息并不是通过PeekMessage和 DispatchMessage来处理的。

猜测2: MsgWaitForMultipleObjects函数返回后,由于不再阻塞消息处理,此时在 PeekMessage同时,消息处理函数处理了 PBM_SETPOS消息。如果是这样的话,我将 PeekMessage和 DispatchMessage改为其它的程序段,比如一些无意义的赋值语句,是否也可以达到相同的效果?实验后发现并不可行。

用debug跟踪了一下程序执行的流程,如下:

SetPos(SendMessage)                   worker thread

--> MsgWaitForMultipleObjects返回       Dlg thread

--> PeekMessage                                         Dlg thread

      

        --> SetPos返回                                     Worker thread

--> PeekMessage返回                                   Dlg threa

根据上面的流程,合理的解释应该是:MsgWaitForMultipleObject等待过程中Worker thread仍然被阻塞,当Dialog中有消息到来时MsgWaitForMultipleObject返回,此时调用PeekMessage,消息处理函数被激活,处理 PBM_SETPOS消息,workder thread中的SetPos函数返回。

然而,究竟是怎么样 PeekMessage会激活消息处理函数?我思考了很久在《windows核心编程》第26章“窗口消息”找到了答案:

对于接收消息的线程而言(本文中为 Dlg thread ),如果该线程正在执行代码,并且没有等待消息 (如调用GetMessage、PeekMessage或WaitMessage)时,发送过来的消息不会被该线程处理,而发送消息的线程(本文中为worker thread)中的SendMessage函数也不会返回。

根据上面的文字,也就是说,在执行 对话框中某个函数时,如果函数没有退出,并且没有执行 GetMessage、PeekMessage或WaitMessage函数,那么从别的线程发送来的SendMessage来的消息将不会被处理。此时再返回看看msdn中对PeekMessage的定义,可能会更加清楚:

Dispatches incoming sent messages, checks the thread message queue for a posted message, and retrieves the message (if any exist).

During this call, the system delivers pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the  SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved.

也就是说,PeekMessage这个函数所做的工作不止是从队列中拿出posted message,还在此之前会先处理nonqueued messages(send message)。

找到了send message的处理流程,也就不难解释上面程序执行的流程了。

还可以看出:MsgWaitForMultipleObjects实际上在这其中并没有起到什么实质性的作用,只是定期返回而已,那么根据此,可以用固定时间的WaitForSingleObject来代替MsgWaitForMultipleObjects ,并且循环等待 ;同时,我们也没有必要手动DispatchMessage,只需要PeekMessage即可,需要注意的是如果删除了 DispatchMessage, 在 PeekMessage时不能将消息从队列取走。 代码如下:

[cpp] view plaincopy print?
  1. DWORD dwRet = 0;
  2. MSG msg;
  3. while (TRUE)
  4. {
  5. dwRet = WaitForSingleObject(m_hThread, 50);
  6. switch(dwRet)
  7. {
  8. case WAIT_OBJECT_0:
  9. break; //break the loop
  10. case WAIT_TIMEOUT :
  11. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  12. continue;
  13. default:
  14. break; // unexpected failure
  15. }
  16. break;
  17. }

DWORD dwRet = 0; MSG msg; while (TRUE) {dwRet = WaitForSingleObject(m_hThread, 50);switch(dwRet){case WAIT_OBJECT_0: break; //break the loopcase WAIT_TIMEOUT :PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);continue;default:break; // unexpected failure}break; }

经过测试,代码运行正确!

当然,由于MsgWaitForMultipleObjects是基于消息驱动的返回,WaitForSingleObject只是普通的定时返回,而本文的情况就是由于消息阻塞造成的,所以MsgWaitForMultipleObjects比WaitForSingleObject在应用时时效性更好,如果改成WaitForSingleObject,在测试中感觉会有略为滞后,所以实际还是以MsgWaitForMultipleObjects为佳。

对话框中WaitForSingleObject等待线程退出导致程序阻塞的原因及解决相关推荐

  1. c语言 执行free函数程序被卡住,FreeRTOS操作系统,在按键中断函数中恢复被挂起的任务,程序卡死的原因和解决办法...

    FreeRTOS操作系统,在按键中断函数中恢复被挂起的任务,程序卡死的原因和解决办法 时间:2019-08-10 14:39:47  来源:  作者:  所属栏目:其他服务端 这里将告诉您FreeRT ...

  2. 无盘服务器0x00000124,知识分享电脑故障0x00000124导致蓝屏的原因和解决思路-电脑蓝屏原因...

    电脑知识分享电脑故障0x00000124导致蓝屏的原因和解决思路 电脑知识分享 电脑故障0x00000124导致蓝屏的原因和解决思路 电脑知识分享 电脑知识分享:蓝屏的原因和解决思路 蓝屏出错代码的缘 ...

  3. 【跑飞、死机】单片机 msp430程序跑飞原因和解决方式积累

    目录 单片机 msp430程序跑飞原因和解决方式积累 MSP430 数组填充越界引起的栈溢出 导致程序跑飞 [单片机重启]MSP430重启/频繁重启/跑飞 原因分析 单片机 msp430程序跑飞原因和 ...

  4. xp系统运行php,window_Xp系统安装或运行软件时提示“EXE不是有效Win32应用程序”的故障原因及解决方法,部分Xp系统用户在双击安装某 - phpStudy...

    Xp系统安装或运行软件时提示"EXE不是有效Win32应用程序"的故障原因及解决方法 部分Xp系统用户在双击安装某个软件时,会遇到"EXE不是有效Win32应用程序&qu ...

  5. libc.so.6被删后导致系统无法使用的原因及解决方法

    libc.so.6被删后导致系统无法使用的原因及解决方法 参考文章: (1)libc.so.6被删后导致系统无法使用的原因及解决方法 (2)https://www.cnblogs.com/weijin ...

  6. ci框架中引入css,php ci框架中加载css和js文件失败的原因和解决方法

    php ci框架中加载css和js文件失败的原因和解决方法 发布时间:2021-07-01 12:08:38 来源:亿速云 阅读:50 作者:chen 本篇内容介绍了"php ci框架中加载 ...

  7. java.exe应用程序出错_EXPLORER.EXE应用程序错误的原因和解决办法

    在使用电脑到时候经常会碰到EXPLORER.EXE应用程序错误,很多人找不到具体原因就乱杀毒和重装系统,这样既浪费时间又麻烦,有时还不一定能解决问题,下面我爱电脑网小编收集导致EXPLORER.EXE ...

  8. U盘插入电脑导致电脑关机的原因及解决办法

    前段时间发生了两次极其尴尬的事情,小编的U盘插到两个人人的电脑上都给人家整蓝屏了,小编搜集了一下,U盘插入电脑导致电脑蓝屏的原因有很多,小编在这里总结并分享一下可能的原因及解决办法: 1.Window ...

  9. Redis中的缓存雪崩、击穿、穿透的原因以及解决办法

    缓存雪崩.击穿.穿透一旦发生,会导致大量的请求积压到数据库层.如果请求的并发量很大,就会导致数据库宕机或是故障,这就是很严重的生产事故了. 俗话说,知己知彼,百战不殆.了解了问题的成因,我们就能够在应 ...

最新文章

  1. 火爆全网!《算法刷题宝典》资源,免费下载!(含代码数据)
  2. pythonsocket自动化教程_python---socket自动化交互
  3. Nvidia、Intel、AMD技术人员薪资大揭秘:平均薪酬超20万$,英伟达最高近35万美元...
  4. 【前沿技术】2021年AI将改变制造业的6大应用趋势
  5. 国内首本Android开发图书之双剑
  6. unity2D限制位置的背景移动补偿效果
  7. for update引发的血案
  8. 怎样使用element-starter快速搭建ElementUI项目
  9. Dapper-开源小型ORM
  10. Xtrabackup备份与恢复
  11. sql server版本号_识别SQL Server版本号的不同方法
  12. Linux的安装及忘记Linux密码的措施
  13. windows 核心编程下的内存映射文件
  14. 知道css有个content属性吗?有什么作用?有什么应用?可以伪类清除浮动
  15. Oracle数据库常见版本
  16. BIN文件和HEX文件区别
  17. MuJoCO仿真(1) MuJoCo210 Win10安装
  18. 2017中国北京艺术与框业展览会(AFAEXPO)会刊(参展商名录)
  19. Centos7搭建lamp环境后外网浏览器不能访问
  20. python京东图书信息抓取

热门文章

  1. layui根据条件显示列_templet渲染layui表格数据的三种方式
  2. 共享虚拟服务器,共享虚拟主机和云服务器
  3. 如何用python绘图、柱形图、线形图等_python使用Plotly绘图工具绘制散点图、线形图...
  4. CodeForces - 1110E-Magic Stones(差分+思维)
  5. 虚拟机centos7 识别不出网卡的解决方案
  6. linux下awk内置函数的使用(split/substr/length)
  7. HDU 4741 Save Labman No.004 计算几何 数学
  8. 部署和调优 1.5 vsftp部署和优化-1
  9. python相关软件安装
  10. 设计模式:代理模式是什么,Spring AOP还和它有关系?