(
						
						
								一)
						
						
								       
						
						
								SendMessage 
						
						
								的工作机制 
						
				
				
						
								首先我要先简要的说明一个和这个话题有关系的消息处理机制: 
						
				
				
						
								
										    
								
						
						
								在Window操作系统当中,窗口时属于所在Thread的也就是说 你这个窗口在那个Thread 当中Create 的那么你这个窗口就属于那个Thread。同时窗口的消息处理函数也都会在这个Thread 当中被执行的。(不要问为什么 Window 就是这么设计的 嘿嘿) 
						
				
				
						
								 
						
				
				
						在讲死锁之前我们先把SendMessage的工作机制搞清楚; 
				
				
						SendMessage 
						发送出来的消息 到底进入不进入消息队列,有人说进入,有人说不进入,其实都是错误的,确切的说是有时进入,有时不进入。那么什么时候进入,什么时候不进入呢? 我们举一例子来说:假如在 Thread A  中有一个 窗口W1,那么 在 Thread A 中像 W1 SendMessage 一个消息,那么这个消息将不会被放入消息队列,而是直接调用了W1的消息处理函数来直接处理了这个消息。这是不被放入队列的情况;假如现在又多了一个Thread B ,那么在 Thread B 中 像 W1 SendMessage 发送消息 这个时候 W1 将被放入到 Thread A 的消息队列当中,这些Thread A 中的消息循环的GetMessage 会Get到这个消息 并处理之。 这就是进入消息队列情况;根据在哪里我们来看看我的测试结果: 
				
				
						 
				
				
						
								测试1:
						
						我创建了一个无DOC/View 之支持的单文档工程: 
				
				
						
								
								
						
						我在CMainFrame添加如下代码: 
				
				
						
								        
						
						BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 
				
				
						
								               ON_WM_CREATE() 
				
				
						
								               ON_WM_SETFOCUS() 
				
				
						
								               
								
										ON_MESSAGE(WM_USER + 100,OnMy) 
								
						
				
				
						
								
										ON_MESSAGE(WM_USER + 200,OnMy2) 
								
						
				
				
						END_MESSAGE_MAP() 
				
				
						 
				
				
						
								LRESULT CMainFrame::OnMy(WPARAM wParam,LPARAM lParam) 
						
				
				
						
								{ 
						
				
				
						
								
										             int i = 0; 
						
				
				
						
								
										             return TRUE; 
						
				
				
						
								} 
						
				
				
						
								 
						
				
				
						
								LRESULT CMainFrame::OnMy2(WPARAM wParam,LPARAM lParam) 
						
				
				
						
								{ 
						
				
				
						
								
										             int i = 2; 
						
				
				
						
								
										             return TRUE; 
						
				
				
						
								} 
						
				
				
						然后我再 
						int
						 CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
						的最后
						
						
						加入了一行代码:
						
								
										
										
								
						
				
				
						SendMessage(WM_USER + 100,0,0); 
				
				
						
								 
						
				
				
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						然后直接 F5 运行程序 等到 程序停止在断点上,我们看看Call Stack 的调用顺序:
				
				
						
								
								
										 此主题相关图片如下:
								
						
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						 
				
				
						然后 我又将 SendMessage 改成: 
				
				
						
								PostMessage(WM_USER + 100,0,0); 
						
				
				
						然后直接 F5 运行程序 等到 程序停止在断点上,我们再看看Call Stack 的调用顺序:
				
				
						
								
								
										 此主题相关图片如下:
								
						
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						通过这2个 Call Stack 大家可以很清楚的看到,执行SendMessage的时候,是直接调用了 AfxWndProcBase这个 消息处理函数(MFC 通过HOOK 将所有窗口的处理函数都重定向到这个 函数上了,AfxWndProcBase()不明白的自己去看《MFC深入浅出》),大家可以很清楚的看到,在SendMessage 到 AfxWindProcBase 之间根本没有调用CWinApp::Run() ,也就是说从SendMessage 到执行OnMy()根本没有通过程序的主消息循环的GetMessage (Run 内部好像用的PeekMessage记不清楚了)取消息。那么有人会问,SendMessage的内部就不会先发消息放入队列再通过GetMesssage把消息取出来了吗?答根本没必要那样做,那是脱裤子放P多此一举。 
				
				
						
								从这个测试例子的结果我判定SendMessage 在 Thread A 中向  W1
						
				
				
						
								SendMessage 
						
						
								的消息根本不进入消息队列。 
						
				
				
						
								测试2:
						
						那么什么时候进入队列呢我来看看这个例子 
				
				
						沿用上面那个例子的代码我将 OnCreate 中的 SendMessage 和 PostMessage 都删除掉。然后加入如下代码: 
				
				
						
								//Thread Proc 
						
				
				
						
								UINT ThreadProc(LPVOID lParam) 
						
				
				
						
								{ 
						
				
				
						
								
										             CMainFrame * v_pFrameWnd = (CMainFrame *)lParam; 
						
				
				
						
								
										             if(v_pFrameWnd) 
						
				
				
						
								
										             { 
						
				
				
						
								
										                v_pFrameWnd->SendMessage(WM_USER + 100,0,0); 
						
				
				
						
								
										             } 
						
				
				
						
								
										             return 0; 
						
				
				
						
								} 
						
				
				
						并且 在 OnCreate 种加入如下代码: 
				
				
						
								AfxBeginThread(ThreadProc,this); 
						
				
				
						然后F5 运行 等待程序停在断点处,看Call Stack 如下:
				
				
						
								
								
										 此主题相关图片如下:
								
						
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						我们发现这个 Call Stack 就和刚才那个PostMessage 的Call Stack 是一样的 这个 WM_USER + 100 消息是通过 Run 内部的 GetMessage 取出来的 。所以我断定: 
				
				
						 
				
				
						
								在 Thread B 中向W1 SendMessage 发送消息 ,消息是放入了 Thread  A 的消息队列中。由于SendMessage的特性只有当消息被执行完毕才能够返回,所以Thread  B 中的SendMessage 要等 Thread A 当中消息执行完毕后才能够返回。 
						
				
				
						
								(
						
						
								一)
						
						
								       
						
						
								SendMessage 
						
						
								产生的 死锁问题 
						
				
				
						Thread 
						死锁肯定是发生在2个Thread 之间,A等B ,B等 A,就产生了死锁。大家看了上面测试之后一定会发现,SendMessage 的死锁和上面的第二个例子有关系,也就是 说 通过 Thread  B 向 W1 发送消息的时候又可能会产生死锁。 
				
				
						
								 
						
				
				
						
								 
						
				
				
						那么死锁 何时产生呢 ?通过上面的例子我们知道了  如果Thread B 向 W1 SendMessage一个消息,那么 Thread B 的这个SendMessage 就要等 Thread A 的队列中的 消息执行完毕才能够返回,如果在 Thread B SendMessage 的同时  Thread A 等待 Thread B 中的某一处理完毕才能够继续处理消息的话,那么这个时候就发送了死锁。 
				
				
						 
				
				
						
								
								
						
						我们继续以测试来说明: 
				
				
						
								
								
						
						测试3: 
				
				
						
								    
						
						首先在 CMainFrame中加入一个 成员变量:
						
								m_bThreadExit 
						
						
								(Public)
						
						
								
										
										
								
						
				
				
						
								    
						
						我们将 UINT ThreadProc(LPVOID lParam) 加入一样代码如下: 
				
				
						
								
										       UINT ThreadProc(LPVOID lParam) 
						
				
				
						
								{ 
						
				
				
						
								
										          CMainFrame * v_pFrameWnd = (CMainFrame *)lParam; 
						
				
				
						
								
										          if(v_pFrameWnd) 
						
				
				
						
								
										          { 
						
				
				
						
								
										             v_pFrameWnd->SendMessage(WM_USER + 100,0,0); 
						
				
				
						
								
										          } 
						
				
				
						
								
										          v_pFrameWnd->m_bThreadExit = TRUE; 
						
				
				
						
								
										          return 0; 
						
				
				
						
								} 
						
				
				
						然后再 OnCreate 当中添加如下代码: 
				
				
						
								
										                             
								
						
						
								m_bThreadExit = FALSE; 
						
				
				
						
								
										          AfxBeginThread(ThreadProc,this); 
						
				
				
						
								
										
										
										 
										
										
								
						
				
				
						
								
										          while(TRUE) 
						
				
				
						
								
										          { 
						
				
				
						
								
										             if(m_bThreadExit) 
						
				
				
						
								
										                break; 
						
				
				
						
								
										    
										         Sleep(55); 
						
				
				
						
								} 
						
				
				
						
								 
						
				
				
						
								 OK 
						编译 F5 运行 发现程序 进入无响应状态,好这时我么让程序 暂停: 
				
				
						看看 2个Thread 的 Call Stack 都停在那里了? 
				
				
						Main Thread
						如下: 
				
				
						
								 
						
				
				
						
								
										 此主题相关图片如下:
								
						
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						在看看 另一个线成: 
				
				
						
								
								
								
										 此主题相关图片如下:
								
						
						此主题相关图片如下:
						
								
								
										 
								
								
								
						
				
				
						
								这会 是不是 很明了了 
						
				
				
						
								MainThread  
						
						
								停在 循环内 等待 m_bThreadExit 为 True,而 另一个线成 则等待 MainThread 处理完毕 WM_USER + 100 这个消息,结果你等我,我等你,死了。。。。 
						
				
				
						
								(
						
						
								一)
						
						
								       
						
						
								处理办法 
						
				
				
						1
						.
						  
						针对上面的例子 我们 可以通过 把SendMessage改成 PostMessage的方法来放弃等待。 这样就解决了 
				
				
						2
						.
						  
						有些时候 第1种方法不符合要求比如下面这中情况 
				
				
						
								UINT ThreadProc(LPVOID lParam) 
						
				
				
						
								{ 
						
				
				
						
								
										          CMainFrame * v_pFrameWnd = (CMainFrame *)lParam; 
						
				
				
						
								
										          if(v_pFrameWnd) 
						
				
				
						
								 
						
				
				
						
								
										          { 
						
				
				
						
								
										          v_pFrameWnd->SetWindowText("lvyang"); 
						
				
				
						
								
										          } 
						
				
				
						
								
										          v_pFrameWnd->m_bThreadExit = TRUE; 
						
				
				
						
								
										          return 0; 
						
				
				
						
								} 
						
				
				
						这里面的CWnd::SetWindowText里面实际上调用的是::SetWindowText 而::SetWindowText 里面有调用 SendMessage 发送一个消息给CWnd 的窗口 ,因为::SetWindowText 内部的我们没有办法来修改,那我只能去修改 MainThread 当中的 While 循环了。 
				
				
						 
				
				
						那如何修改呢? ThreadProc 当中 SetWindowText之所以被诸塞,就是因为 它向 MainThread SendMessage 的消息没有得到处理,那么我们让他处理的不就OK了吗?好那我们就让他处理,代码如下: 
				
				
						
								MSG msg; 
						
				
				
						
								
										       while(TRUE) 
						
				
				
						
								
										       { 
						
				
				
						
								
										          if(m_bThreadExit) 
						
				
				
						
								
										             break; 
						
				
				
						
								
										          
								
						
						
								
										if(::PeekMessage(&msg,NULL,NULL,NULL,PM_NOREMOVE)) 
								
						
				
				
						
								
										
												          { 
								
						
				
				
						
								
										
												             if(::GetMessage(&msg,NULL,NULL,NULL)) 
								
						
				
				
						
								
										
												             { 
								
						
				
				
						
								
										
												                if(!PreTranslateMessage(&msg)) 
								
						
				
				
						
								
										
												                { 
								
						
				
				
						
								
										
												                   ::TranslateMessage(&msg); 
								
						
				
				
						
								
										
												                   ::DispatchMessage(&msg); 
								
						
				
				
						
								
										
												                } 
								
						
				
				
						
								
										
												             } 
								
						
				
				
						
								
										
												          } 
								
						
				
				
						
								
										    
										      Sleep(55); 
						
				
				
						
								} 
						
				
				
						
								终于搞完了