在主线程中慎用WaitForSingleObject (WaitForMultipleObjects)

下面的代码我调试了将近一个星期,你能够看出什么地方出了问题吗?

线程函数:

DWORD WINAPI ThreadProc(
    
while(!bTerminate)
    
{
        
// 从一个链表中读取信息并且插入到CListCtrl中
        
// CListCtrl的句柄是通过线程参数传递进来的
        for(;;)
       
{
           ReadInfoFromList();
           InsertToCListCtrl();
        }

    }

}

主线程中使用CreateThread启动线程。

当想终止子线程时,在主线程中:
bTerminate = TRUE;
WaitForSingleObject(threadHandle, INFINITE);
可是,以运行到WaitForSingleObject,子线程就Crash了。

为什么呢?

问题原因:
后来我终于在InsertItem的反汇编中发现了如下的代码
call dword ptr [__imp__SendMessageA@16 (7C141B54h)]
可见,InsertItem是必须借助消息循环来完成任务的。如果我们在主线程中WaitForSingleObject了,必然导致主线程阻塞,也就导致了消息循环的阻塞,最终导致工作线程Crash掉了*_*

解决方案:
为了解决在主线程中Wait的问题,微软专门设计了一个函数MsgWaitForMultipleObjects,这个函数即可以等待信号(thread,event,mutex等等),也可以等待消息(MSG)。即不论有信号被激发或者有消息到来,此函数都可以返回。呵呵,那么我的解决办法也就出来了。
将上面的WaitForSingleObject用下面的代码替换:

while(TRUE)
{

    DWORD result ; 
    MSG msg ; 

    result 
= MsgWaitForMultipleObjects(1&readThreadHandle, 
        FALSE, INFINITE, QS_ALLINPUT); 

    
if (result == (WAIT_OBJECT_0))
    
{
        
break;
    }
 
    
else 
    

        PeekMessage(
&msg, NULL, 00, PM_REMOVE);
        DispatchMessage(
&msg); 
    }
 
}


总结:
如果在工作线程中有可能涉及到了消息驱动的API,那么不能在主线程中使用WaitForSingleObject一类函数,而必须使用上述的方案。

posted on 2004-07-15 20:22 shootingstars 阅读(13629) 评论(26)  编辑 收藏 网摘 所属分类: C++


评论

#1楼  2004-09-17 11:38 HXY [未注册用户]

谢谢你的文章!

  回复  引用    

#2楼  2004-12-10 11:00 Jeff [未注册用户]
太强了,解决了困扰我一整天的问题!!!
  回复  引用    

#3楼  2005-02-27 16:06 world [未注册用户]

作者你好,

消息循环只负责获取消息队列中的消息 ,
SendMessageA的消息并不进入消息队列


  回复  引用    

#4楼  2005-11-19 02:09 msctor [未注册用户]
看了你的文章后,问题解决,非常感谢。
  回复  引用    

#5楼  2005-12-15 17:44 wuzq [未注册用户]
您好!我对你写的内容好奇,于是我就按照你的做法在windows32的console程序,
并没有出现你说的事情,线程依然很好干了他该干的事。我想可能是你搞错了,导致死锁了吧。

  回复  引用    

#6楼 [楼主] 2006-01-17 10:10 shootingstars      
To wuzq
不知道你是如何做的测试,能够把测试代码贴出来吗?

  回复  引用  查看    

#7楼 [楼主] 2006-01-17 10:37 shootingstars      
To world
SendMessageA发送的消息确实不进入消息循环,但是这个过程是在主线程的上下文中完成的,还是在开的线程中完成的?
呵呵,有空再研究研究,或许原理还是有问题。

  回复  引用  查看    

#8楼  2006-01-18 16:32 蠕虫 [未注册用户]
console不会是因为他没有消息循环的原因吧。猜想你的程序可能是创建一个线程之后就让它在console停在那里了对吧,这是正常的,但是在有窗口消息的程序中就不行了。
我是一个初学者,有VC高手希望能交个朋友。
我的E-MAIL:puma-ly@163.com

  回复  引用    

#9楼  2006-03-28 16:27 za [未注册用户]
非常感谢!!!,
  回复  引用    

#10楼  2006-06-18 20:10 mjk [未注册用户]
解决了我2天百思不得其解的问题, 谢谢.
  回复  引用    

#11楼  2006-11-27 09:21 cm [未注册用户]
谢谢!帮我解决了一个问题
  回复  引用    

#12楼  2006-11-27 09:21 cm [未注册用户]
谢谢!帮我解决了一个问题
  回复  引用    

#13楼  2006-11-27 09:21 cm [未注册用户]
谢谢!帮我解决了一个问题
  回复  引用    

#14楼  2006-12-03 01:12 Stzpz [未注册用户]
直接把for(;;)去掉就可以了吧。那样while循环条件不成立时函数就返回了,WaitForSingleObject也就返回了。
  回复  引用    

#15楼  2007-01-25 11:15 aGAric [未注册用户]
SendMessage的目标窗口如果属于另一个线程,则会发生线程上下文切换,等待另一线程处理完成消息。为了防止另一线程当掉,导致SendMessage永远不能返回,我们可以调用SendMessageTimeout函数

 

  回复  引用    

#16楼  2007-07-19 16:32 IfI [未注册用户]
太感谢了,困扰我好几个礼拜的问题啊....

不过在WaitForSingleObject的第二个参数设置成一个数值也可以避免出问题,不过没有楼主的效果好~

  回复  引用    

#17楼  2007-10-06 19:25 zhcen [未注册用户]
我要在主线程中开启一个UI线程,该UI线程查找某种设备,所以主线程要阻塞,一直到UI线程结束,我该怎么办呢
  回复  引用    

#18楼 [楼主] 2007-10-08 13:29 shootingstars      
To zhcen
你的主线程不是一个UI线程吗?
我的建议是:在UI线程中等待某个事件发生一定使用MsgWaitForMultipleObjects,不要使用WaitForMultipleObjects。

  回复  引用  查看    

是你自己字线程本身代码有问题,不是WaitForSingleObject的错
for没办法退出,怎么WaitForSingleObject?

  回复  引用    

你好,看了你的代码后对我帮助很大,但同时也发现了个很奇怪的问题。
在debug版本下面正常运行
但如果在release版本下,你的等待那个函数
while(TRUE)
{
DWORD result ;
MSG msg ;
result = MsgWaitForMultipleObjects(1, &readThreadHandle,
FALSE, INFINITE, QS_ALLINPUT);

if (result == (WAIT_OBJECT_0))
{
break;
}
else
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
DispatchMessage(&msg);
}
}
好像就没起作用了。
我估计是不是函数的返回值有问题。。。不太清楚,希望你能帮忙看下,
谢谢!!

  回复  引用    

#21楼  2007-12-25 18:35 Phrea [未注册用户]
微软的文档写得清清楚楚啦,凡是创建窗口的线程必须谨慎使用该函数!
  回复  引用    

LZ看了你的文章后有些疑惑的,

1. 的线程代码中的 for(;;){
ReadInfoFromList();
InsertToCListCtrl();
}
好像没有必要要for(;;){}这一层吧,这样的话你的while(!bTerminate)就没有什么意义了,永远没法执行到吧,

2. result = MsgWaitForMultipleObjects(1, &readThreadHandle, FALSE, INFINITE, QS_ALLINPUT); 如果你用INFINITE永远等待不超时后面的代码在在等待的时间没有发生的情况下还能执行到吗?

  回复  引用    

#23楼  2008-03-25 19:22 梦书      
太感谢了!
  回复  引用  查看    

22楼白痴。
回答你
1,人家那是简写。难道楼主故意写个死循环在那么?

2,你根本就没看懂。人家说是有消息来也可以往下执行。知道什么是有消息么?

  回复  引用    

#25楼  2008-07-24 12:38 fvvdv [未注册用户]
楼主搞错了。
CListCtrl 只能在主线程操作。
WaitForSingleObject 没问题。

  回复  引用    

WaitForSingleObject(threadHandle, INFINITE);中INFINITE改为0也可以返回的。
原文地址:https://www.cnblogs.com/zhangzhifeng/p/2150147.html