MFC的消息处理是单线程还是多线程的?消息处理函数会重入吗?

spwnihao 2005-12-13 09:10:18
我记得好像mfc处理是单线程的了,
一个简单的处理WM_TIMER消息,在处理方法中,加入 (全局初始化g_bool = false)
if(g_bool == false)
{
AfxMessageBox(L"aaa");
g_bool = true;
}
如果timer触发太快,来不及关掉messagebox,就会不停的弹出多个,直到程序挂掉了
...全文
865 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
chw_csdn_chw 2005-12-14
  • 打赏
  • 举报
回复
楼主的问题问的太帅了吧?。。。。。。
首先timer事件与线程绝对不是一码事,一个线程可以产生N个窗体,可以产生N个timer,所有这些窗体,Timer都对应该线程的消息队列,程序的消息处理核心GetMessage,TranslateMessage,DispatchMessage不断从队列中取出消息,经由操作系统调用回调消息处理函数处理。
其次虽然弹出很多模式窗体,但是这些窗体仅能使父窗体无效,不能接受输入,但是不会阻塞MFC程序的消息循环。当定时器设置后,windows按间隔向当前线程的消息队列中放置WM_TIMER消息,消息循环对WM_TIMER消息进行处理,所有才会一直弹出对话框。当然点击一个对话框后进入死循环,程序的消息循环得不到执行就死掉了。
我用delphi做了个test

timer1.inteval=1000;
timer2.inteval=5000;
procedure TForm1.Timer1Timer(Sender: TObject);
begin
messagebox(handle,'a','b',0);
end;

procedure TForm1.Timer2Timer(Sender: TObject);
begin
application.Terminate;
end;
在弹出5个窗体后程序就退出了,这证明了modal窗体不会阻塞程序的消息循环,(不过好像windows 3.1中好像会阻塞其他进程,win32具有强制多任务的功能,不会对其他进程产生影响)

另外,线程中如果涉及窗口操作,那么线程必须创建消息队列。线程可以没有消息队列。
越说越多了...不说了,工作了.


fdimim 2005-12-14
  • 打赏
  • 举报
回复
mark
alen_ghl 2005-12-13
  • 打赏
  • 举报
回复
gz
cici2006 2005-12-13
  • 打赏
  • 举报
回复
线程应该是不会因为你的弹出MESSGBOX而停止吧。只是你的主程序不能响应你的鼠标键标消息吧。
spwnihao 2005-12-13
  • 打赏
  • 举报
回复
楼上的老大,还没有告诉我,mfc的消息函数会不会重入啊?是不是多线程啊?
我的理解是Mfc是单线程的一个主函数负责消息的分发,如果一个消息阻塞了,那么其他的消息也就不能相应了,我的问题是在timer方法中弹出了messagebox,由于没有来得及关闭,当然消息也就没有返回,第二个timer消息到达了,这说明同时有两个time方法在运行,那么mfc是多线程了,呵呵,不懂啊,老大赐教啊
快乐鹦鹉 2005-12-13
  • 打赏
  • 举报
回复
没错,是这样的。AfxMessageBox弹出的窗口,本身也有消息循环。当一个对话框弹出后,什么也不做时,定时器是会响应的,但是如果弹出对话框一直在工作,假设是个死循环之类的,那么定时器就不会响应。
Atomictry 2005-12-13
  • 打赏
  • 举报
回复
MFC下的源码可以看这个几个核心文件:
WNDCORE.CPP, THRDCORE.CPP, WINMAIN.CPP,DLGCORE.CPP,还CWinApp这个类的文件。是单线程还有一点左证:
MFC框架生成的CYourApp theApp;永远只有一个,它在int nResponse = dlg.DoModal();这个函数的进入到DLGCORE.CPP中RunModalLoop循环。
Atomictry 2005-12-13
  • 打赏
  • 举报
回复
是单线程,请看MFC的CWinApp类的实现!
继承关系如下:
CXXXApp -> CWinApp -> CWinThread,MFC框架就这么一个线程,消息都来自这个线程分发。

看CWnd级代码:(程序一直在这个循环工作)
int CWnd::RunModalLoop(DWORD dwFlags)
{
for (;;)
{
// phase1: 处理空闲work
while (bIdle &&
!::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE))
{
// code...,这里有一处可以让循环退出的bool变量.
}

// phase2: 取出messages 当可用的时候,最后程序一直在这个循环工作
do
{
// pump message, but quit on WM_QUIT
// 这里PumpMessage()函数将CWinThread级的自己的PreTranslateMessage函数
if (!AfxGetThread()->PumpMessage()) // CWinThread级的代码
{
AfxPostQuitMessage(0);
return -1;
}
}
}while (::PeekMessage(pMsg, NULL, NULL, NULL, PM_NOREMOVE));
}

在看CWinThread级的代码:
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
{
}
// process this message, CWinThread级的自己的PreTranslateMessage函数开始调用
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}

}

// CWinThread级的PreTranslateMessage函数开始调用
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
CWnd* pMainWnd = AfxGetMainWnd();
// 注意WalkPreTranslateTree这个函数,这个函数会根据MSG的参数得到该消息窗口句柄,
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
return TRUE;
// other code

CWnd* pMainWnd = AfxGetMainWnd();

return pMainWnd->PreTranslateMessage(pMsg); //
}

// 这个函数大概就是关键,也就是说虽然有MessageBox对话框弹出,但是不妨碍消息发送到其他窗口啊
BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
{
// walk from the target window up to the hWndStop window checking
// if any window wants to translate this message
for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
if (pWnd != NULL)
{
// target window is a C++ window
if (pWnd->PreTranslateMessage(pMsg)) // 处理它自己的消息
return TRUE; // trapped by target window (eg: accelerators)
}

// got to hWndStop window without interest
//一直到顶层窗口
if (hWnd == hWndStop)
break;
}
return FALSE; // no special processing
}
wangk 2005-12-13
  • 打赏
  • 举报
回复
不好说吧,2000和XP好像不一样。
有的在2000下的驱动(文件系统驱动)等待的时候会把当前线程阻塞,而XP不会。
Featured 2005-12-13
  • 打赏
  • 举报
回复
不知道回调函数是不是同样的原理
我正在做驱动,发现即使当前线程WaitForSingleObject而挂住了,回调函数也能被执行到
wangk 2005-12-13
  • 打赏
  • 举报
回复
好了,我做了试验:
在OnTimer函数中用下面的代码:
static int iCount = 0;
TRACE("Recive %d Count Timer Message!\n" , ++iCount);
//Sleep(5000);
if ( iCount < 15 )
{
Sleep(5000);
//AfxMessageBox("Test");
}
用MessageBox时发现,Output窗口每一秒都输出一次。
但是用Sleep(5000);时在前面的15次每5秒输出一次,之后开始正常。

于是得到结论:
MessageBox应该是有自己的消息循环,通常不会阻塞MFC程序的消息循环。
但是实际上MFC是单线程的。
那么楼主可以得到更详细的推论了吧。
^_^
打雷啦的专栏 2005-12-13
  • 打赏
  • 举报
回复
上面A,B函数均是继承自CWinThread线程类的成员函数.
打雷啦的专栏 2005-12-13
  • 打赏
  • 举报
回复
我也想问一个问题:
情况1:我在一个继承自CWinThread的一个线程,如果这个线程里的函数A,Sleep(5000),然后我在主线程里调用函数B,那么B函数是否马上运行?
情况2:还有,如果sleep换成SendMessage,阻塞在那里,那么主线程调用函数B,是否马上执行?
想到楼主的问题,对话框阻塞了主线程,但是程序的定时器还在工作,所以我想定时器是一个线程,不关主线程的事情.我也困惑了很久,看到楼上几位红星老大的说法,感觉还没没能回答楼主的问题,就是为什么没有阻塞,还是进入了定时器.
shmily1280 2005-12-13
  • 打赏
  • 举报
回复
就是重发同一个消息吧
xiaoqiqixiao(七七) 讲的真清楚^^
advancedchan 2005-12-13
  • 打赏
  • 举报
回复
以我個人理解MFC的消息处理是單單线程的,,    
oyljerry 2005-12-13
  • 打赏
  • 举报
回复
就是多次访问线程中的变量等,如全局变量等,可能多个线程访问时,造成变量不安全
wangk 2005-12-13
  • 打赏
  • 举报
回复
弱弱的问:重入是什么意思啊?
xiaoqiqixiao 2005-12-13
  • 打赏
  • 举报
回复
happyparrot(快乐鹦鹉)已经解释清楚了,请再仔细看看,是单线程的


MFC中窗口类(比如C**View,CFrameWnd等)中的MessageBox函数,以及AfxMessageBox函数都是阻塞原有的消息循环的。由消息框内部的一个消息循环来从消息队列中读取消息,并派送消息(和模式对话框类似)。实际上,这些消息函数最终调用的是::MessageBox,它在消息框内部实现了一个消息循环(原有的主程序消息循环被阻塞了)。论坛中碰到过几次关于计时器和消息框的问题,看下面的代码:



void CTest_recalclayoutView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
MessageBox("abc");
while(1); //设计一个死循环
CView::OnTimer(nIDEvent);
}


咱让OnTimer大约5秒钟弹出一个消息框。那么,消息框不断的被弹出来,只要消息框不被关闭,那么程序就不会进入死循环。实际上,每次弹出对话框,都是最上层的那个消息框掌握着消息循环,其他的消息循环被阻塞了。只要不关闭最上面的消息框,while(1);就得不到执行。如果点了关闭,程序就进入了死循环,只能用ctrl+alt+del来解决问题了。
spwnihao 2005-12-13
  • 打赏
  • 举报
回复
自己顶,郁闷!200的问题没人回答,
顶的有小分答谢

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧