示意代码:
CriticalSection g_Section;
CDialog g_Dlg;// 工作线程函数
UINT TreadFunc_A(PVOID para){ Sleep(10); g_Section.Lock();Sleep(3000);
g_Dlg.m_Show_Edit.SetWindowText("TreadFuncA");// Beep(400,20);g_Section.Unlock();
}// 窗口按钮消息函数
void CDialog::OnButton1() { g_Section.Lock();g_Dlg.m_Show_Edit.SetWindowText("OnButton1");
// Beep(800,20); g_Section.Unlock();}死锁出现条件:
TreadFunc_A 进入 g_Section.Lock() 临界区在没有释放临界锁之前OnButton1()触发,g_Section.Lock() 阻塞等待....永远都等不到了!实验:
如果把g_Dlg.m_Show_Edit.SetWindowText替换成Beep,测滴答滴答的运行OK,不会出现死锁。看来问题出在SetWindowText上了。分析:
导致死锁的一个隐含机制:SetWindowText 的实现使用了 SendMessage(WM_SETTEXT,...)而SendMessage是阻塞调用,等待窗口处理函数完成才返回。同时OnButton1也是窗口消息处理函数的回调映射,所以 g_Dlg.m_Show_Edit.SetWindowText("TreadFuncA"),在等待窗口消息处理函数的返回,线程被阻塞。而OnButton1占用了窗口消息函数执行,申请临界锁被阻塞。这样工作线程TreadFunc_A 占用临界锁同时等待窗口消息处理函数的返回,而界面线程占用窗口消息处理函数同时等待临界锁,于是就永远死翘翘的等下去了。结束语:
MFC下面厚厚的封装,鬼才知道是什么实现机制。为了吃块糖,只需要剥开糖纸即可,为什么还要了解糖的加工工艺。吃别人的东西,直管往嘴里放就得了,出了问题再想办法,也只能这么着了。扩展阅读:
1)Windows 窗口消息原理
2)SendMessage的实现机制
3)MFC 消息映射框架