posts - 256, comments - 1319, trackbacks - 41, articles - 8
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

MFC全接触(三)

Posted on 2004-09-11 23:44 FantasySoft 阅读(2070) 评论(6)  编辑 收藏 所属分类: All About SoftOnly Windows

       昨天"问题男"老大给我的那篇post:噢,我的第一个基于SDK的窗口写了回复:“为何要PeekMessage呢?这样会令你的程序占用能占用的所有cpu时间,不如GetMessage,在没有消息时线程将被suspend。”这个回复让我思考了很久,也让我有了更多收获。真的很喜欢这种感觉,自己写下了一些简单而不成熟的想法,高手看到了又不吝赐教,正是大家这种知识共享的精神让我每天都有莫大的收获,真的很感谢!有点跑题了,呵呵~~
        说实在的,当时在运行自己写的例子的时候,确实发现CPU时间被耗尽了,系统运行得很慢,当时还真的没有反
应过来是怎么回事,而且由于当时关注的问题并不在此,也没有去多想,只是想着随后要看一下基于MFC的程序运行会不会也是占用那么多的CPU时间(虽然当时我就知道答案肯定是不会的,呵呵)。直至看到了以上回复的时候,脑袋瓜受到了激发,一些原先完全割裂开的认识被这个回复联系到了一起。
        首先,刚开始接触PeekMessage和GetMessage的时候,文档告诉我PeekMessage是一个具有线程异步行为的函数,
不管消息队列中是否有消息的,函数都会立即返回。而GetMessage则是一个具有线程同步行为的函数,如果消息队列中没有消息的话,函数就会一直等待,直到消息队列中至少有一条消息的时候,才会返回。先不管线程同步或者异步的话题,光看PeekMessage无论什么情况下都会返回,就会知道例子中的消息处理代码在消息队列中没有消息的时候,会不断的循环执行,就相当一个死循环,CPU不耗尽才怪呢。
        其次,正是"问题男"老大的回复让我想起了MFC中的消息处理机制:在那里不也用了PeekMessage函数到消息队列中取
消息吗?那她到底是怎样避免死循环的出现的呢?于是,我再次回到CWinThread类中的Run函数中去看个究竟:

for (;;)
{
    
// phase1: check to see if we can do idle work

    while (bIdle &&!::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))
    
{
        
// call OnIdle while in bIdle state

    if (!OnIdle(lIdleCount++))
        bIdle 
= FALSE; // assume "no idle" state

    }


    
// phase2: pump messages while available
    do
    
{
        
// pump message, but quit on WM_QUIT

    if (!PumpMessage())
        
return
 ExitInstance();

    
// reset "no idle" state after pumping "normal" message

    if (IsIdleMessage(&m_msgCur))
    
{
        bIdle 
=
 TRUE;
        lIdleCount 
= 0
;
    }


    }
 while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));
}
    

        代码中的bIdle的初始值是TRUE,如果PeekMessage返回值为FALSE,也就是当队列中没有消息的时候,那么while中的整个条件语句为TRUE,就会去调用OnIdle函数。由于lIdleCountOnIdle作为OnIdle函数的入参,且初始值为0,而返回的语句则是return lCount < 0,也就是说OnIdle返回值肯定是FALSE,那么bIdle就被赋值为FALSE了,自然就跳出了循环。接着就再进入下一个循环,通过PumpMessage方法去取得消息,而PumpMessage中实质就是调用了GetMessage方法去获得消息。总之在两个循环当中,PeekMessage的作用就象是探路石一样,去check一下队列中的是否有消息,有的话,是怎样的消息,但是她并没有将消息从队列中移除,我们可以通过PeekMessage方法中的最后一个参数就可以看出了。同时,两个循环也是交替进行的,象第二个循环,当消息队列中没有消息的时候,也会再一次回到一个循环。MFC之所以这样设计是希望能够通过OnIdle方法去处理当该窗体所属的线程处于空闲状态的情况,同时也保证了线程不会因为GetMessage而进入休眠状态。
  

Feedback

#1楼    回复  引用    

2006-06-29 10:11 by 不是很理解 [未注册用户]
对你文中的"同时也保证了线程不会因为GetMessage而进入休眠状态"不是很理解,能给指导一下吗,谢谢.

#2楼 [楼主]   回复  引用  查看    

2006-06-29 10:38 by FantasySoft      
@不是很理解
因为GetMessage是一个具有线程同步特性的方法,如果它没有从消息队列中获得消息,它就不会返回,一直处于等待状态,那么GetMessage所在的线程也就进入休眠状态了。一般的应用程序还是使用PeekMessage去取消息,因为它是一个异步的方法,不管是否取得消息,它都会返回的,这样就避免了线程进入休眠状态,但是要防止出现死循环。

#3楼    回复  引用    

2006-06-29 18:41 by 不是很理解 [未注册用户]
我想问一下,他是如何避免出现死循环的啊?

#4楼 [楼主]   回复  引用  查看    

2006-06-29 20:45 by FantasySoft      
@不是很理解
通过bIdle这个布尔型变量啊,我在文中有讲到的。

#5楼    回复  引用    

2006-06-30 15:48 by 不是很理解 [未注册用户]
好的;那你分析一下,下面的代码能达到相同的功能嘛?顺便问一下IsIdleMessage(&m_msgCur)的含义是什么啊???

bool idle=true;
long count=0;
for(;;)
{
if(::peekMessage(&msgCur,NULL,0,0,PM_NOME))
{
if(!GetMessage(&msgCur,NULL,0,0))
return msgCur.wParam;

if(!PreTranslateMessage(&msgCur))
{
::TranslateMessage(&msgCur);
::DispatchMessage(&msgCur);
}
idle=true;
count=0;
}
else if(idle)
{
if(OnIdle(count++)
idle=false;
}
else
{
::WaitMessage();
}
}

#6楼 [楼主]   回复  引用  查看    

2006-06-30 16:02 by FantasySoft      
@不是很理解
不好意思,已经两年多没有看MFC了,没有办法回答你的第一个问题。至于第二个问题,你可以查阅MSDN。

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2005-09-21 14:54 编辑过
 
另存  打印