首页
社区
课程
招聘
MFC 线程于消息循环的困惑
发表于: 2010-5-11 21:23 39450

MFC 线程于消息循环的困惑

2010-5-11 21:23
39450
Pro1:我们用MFC建一个空的基于Dialog的对话框程序,然后跑起来,这样是单线程还是2个线程?
Pro2:是否我们的按钮函数没有返回之前消息循环就是一直阻塞的?

ok,看下面这样一个例子
#include "process.h"
//ON_MESSAGE(WM_USER+1, OnMyMess)
LRESULT CMyDlg::OnMyMess(WPARAM wparam, LPARAM lparam)
{
	exit(0);
	return 1;
}
UINT __stdcall myThread(void *p)
{
	HWND *pHand = (HWND *)p;
	SendMessage(*pHand, WM_USER+1, 0, 0);
	return 0;
}

void CMyDlg::OnOK() 
{
	_beginthreadex(NULL, 0, myThread, (void *)&m_hWnd, 0, 0);
	//AfxMessageBox("Just for try!");
	Sleep(2000);
}


我们发现在Ok按钮里的Sleep时间内(也就是OnOk函数返回前)  myThread的SendMessage被阻塞的  没有响应消息循环。

好了,下面我们在Sleep前面加上一个MessageBox, 哇 发现什么了?消息循环不阻塞了  请问是为什么?

非常感谢!  下面是上面例子的附件。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (13)
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
1)单线程

2)不一定:
    如果在消息处理过程中使用PeekMessage、GetMessage例程,这些例程会检测其它线程发送过来的消息,如果有就立刻处理。
    在消息处理过程中还可以使用DispatchMessage处理通过PeekMessage或GetMessage从消息队列中取得的消息。
2010-5-11 22:22
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
3
麻烦您可以试着查一下任务管理器。
2010-5-12 09:46
0
雪    币: 184
活跃值: (41)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
我当然是查过的,注意是空的对话框程序(你的例子创建了线程,当然不是单线程),难道你的机器查出来不是一个线程?
2010-5-12 10:34
0
雪    币: 724
活跃值: (81)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
关于问题1,2楼回答的应该没错,如果你机器上有两个线程,则可能是因为你机器中有其它软件注入到了你的进程中而引起的。
关于问题2,也可以说是的,因为消息队列中的消息要消息循环处理,但SendMessage产生的消息是不进消息队列的,如果你用PostMessage,则生产的消息要等你的OnOk返回后才能得到处理。
2010-5-12 10:41
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
6
回4楼:我查的空的对话框就是2个线程,原因可能是5楼说的原因,待查~~
回5楼:SendMessage产生的消息是不进入消息队列的  有点不妥当~~
找了一点资料:
http://blog.csdn.net/yfqvip/archive/2008/12/30/3644774.aspx
2010-5-12 11:14
0
雪    币: 249
活跃值: (25)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
SendMessage应该是不进OS的消息队列,直接进入处理函数的消息队列~~很久没在做界面编程,希望没记错。
2010-5-12 11:23
0
雪    币: 724
活跃值: (81)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
8
我想楼主是指的是“一个线程的消息队列实际上分为四种不同的消息队列:Post消息队列、Send消息队列、输入消息队列、应答消息队列。PostMessage是将消息追加到Post消息队列,SendMessage是追加到Send消息队列,两个队列处理的优先级并不一样”
这种提法并非Microsoft的提法,SDK文档中所说的消息队列应该是上面说的Post消息队列。关于SendMessage, MSDN有如下描述:
This function sends the specified message to a window or windows. SendMessage calls the window procedure for the specified window and does not return until the window procedure has processed the message. The PostMessage function, in contrast, posts a message to a thread's message queue and returns immediately.
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message.
所以,要看你的“消息队列”是如何定义的了。
2010-5-12 11:44
0
雪    币: 75
活跃值: (688)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
9
看大家讨论的这么热烈,我也得凑凑热闹

sendmessage()也是进消息队列的,线程的视窗报文队列实际上是包含了7个不同用途的报文队列
SentMesagesListHead,sendmessage()产生的报文就是挂在这个队列里
PostedMessageListHead
......
其中sendmessage()产生的报文有点特殊,它们是不能被PeekMessage() GetMessage()返回,由系统内部处理。
若消息处理线程在getmessage()函数这里阻塞了,表面上看,好像系统没有接收到任何报文。实际上若是收到sendmessge()发送来的报文,系统会立即处理,然后恢复阻塞状态。直到系统收到其他类别的报文(post报文,hardmessage报文等),getmessage()才返回。
2010-5-12 11:57
0
雪    币: 95
活跃值: (419)
能力值: ( LV9,RANK:310 )
在线值:
发帖
回帖
粉丝
10
以前我也有这个疑惑,见帖子http://bbs.pediy.com/showthread.php?t=107794

你上面的例子里,在没加MessageBox语句时,只有源程序的主消息循环,在Ok按钮消息处理没返回时,这个消息循环都是阻塞的,在加入了MessageBox语句后,因为弹出的消息框本身就会有正常的消息循环,虽然这时主消息循环阻塞,但这个时候系统会将主窗口消息直接发送到主窗口处理过程,不用经过主消息循环的GetMessage,DispatchMessage的
2010-5-12 19:52
0
雪    币: 97
活跃值: (43)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
11
晕,这个问题怎么讨论这么久时间。我说一下你的情况。
Pro1:我们用MFC建一个空的基于Dialog的对话框程序,然后跑起来,这样是单线程还是2个线程?

答:单线程,也就是主UI线程,可以接收消息循环的线程(很特殊,因为在Windows中,接收消息的实体是窗口)。

Pro2:是否我们的按钮函数没有返回之前消息循环就是一直阻塞的?

答:这个问题结合你的代码回答。
当不加AfxMessageBox("Just for try!");这条语句时:
你是说线程发的消息被堵塞了(当然了,因为你在线程中发送的消息需要传递到主UI线程的消息处理队列,才能被处理,而现在你的UI线程都在Sleep睡觉呢,根本就没去理会消息队列,若是你的Sleep里的时间很长,你会发现你的对话框界面死在那里,在进程管理器里显示“未响应”)。

那么是不是你的主线程被堵塞了,就表示你的程序不工作呢?错,只要你编写的程序还有其他线程,他还是在工作的,因为操作系统的任务调度器会把CPU的时间片分给你的其他线程。也就是你程序中的UINT __stdcall myThread(void *p)。但是你说的没有响应消息,并不代表你的myThread线程没工作,他是工作了的,也就是SendMessage(*pHand, WM_USER+1, 0, 0);这条语句被执行了,你要是不相信,可以再SendMessage(*pHand, WM_USER+1, 0, 0);的前后都加入这条语句:
UINT __stdcall myThread(void *p)
{
  HWND *pHand = (HWND *)p;
  TRACE("进入线程");
  SendMessage(*pHand, WM_USER+1, 0, 0);
  TRACE("消息发送完成");
  //可以使用AfxMessageBox("线程完成");//很讨厌使用对话框,虽然有时候很有用,但是一到这里就阻断程序运行了,监控不了时实行。
  return 0;
}



上面的TRACE可以使用DbgView查看到调试信息,其实VC的调试模式也能够看到,呵呵。

好了,再来回到你说的,若是加了AfxMessageBox("Just for try!");这条语句,你好像发现新大陆似的,能够接收到消息,并且处理。那还用你说。在你弹出对话框时,操作系统的任务调度就把CPU时间片分给了你程序的其他线程myThread,他获得CPU时间,那么他就会发送消息,你点击按钮之后,同时在执行Sleep之前,主UI线程就会去处理myThread线程发送的消息。所以你会发现你的消息被处理了。

其实你只要明白操作系统的任务调度的机制,他是具有优先级的抢占式调度的。所以很有可能在主UI线程创建线程后,CPU的时间根本就没有分到你创建的线程,而是还被主UI线程使用着,所以会去执行_beginthreadex(NULL, 0, myThread, (void *)&m_hWnd, 0, 0);这条语句的下一条语句Sleep(2000);,而这个时候,在主UI线程睡觉的时候,CPU时间让给了你创建的线程myThread,然后进行消息发送,但是,消息发完了,主UI线程还在睡觉,怎么可能给你应答呢?是不是这样的,哈哈哈哈
2010-5-13 10:27
0
雪    币: 97
活跃值: (43)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
忘了再加一句,你发送的消息处理的是exit(0);他直接把主线程杀死,那么其他辅助线程也跟着死去。你调试发现,SendMessage后面的代码都不执行了,而且你主线程的Sleep也跟着死亡。在Windows中,消息循环是采用回调函数机制来完成的。也就是可以理解为
LRESULT CMyDlg::OnMyMess(WPARAM wparam, LPARAM lparam)
{
  exit(0);
  return 1;
}

你的消息响应函数是工作在另一个线程的,只不过这个线程不是被你调用,而是被系统内部的线程调用。这就能解释你的困惑了
其实说白了就是两个消息:一个按钮的,一个是你自定义,谁先抢到CPU的使用权。
而exit(0)是结束整个进程,进程是线程的宿主,没了进程,线程何在?
2010-5-13 12:00
0
雪    币: 458
活跃值: (421)
能力值: ( LV9,RANK:610 )
在线值:
发帖
回帖
粉丝
13
回楼上  :
非常详细的回复!谢谢了~
2010-5-13 16:35
0
雪    币: 97
活跃值: (43)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
不客气,我认为别老看结果,眼睛看到的也许是个假象,而是应该是动手调试调试,在VC中多使用TRACE之类的打印信息,然后看一下执行的先后顺序,非常适合理解多线程和消息循环。
2010-5-13 19:02
0
游客
登录 | 注册 方可回帖
返回
//