线程与消息
更新时间:2024-06-09 07:34:01 阅读量: 综合文库 文档下载
在前面我们研究了使用AFX_MANAGE_STATE(AfxGetStaticModuleState())进行DLL间的资源切换,以及工作线程中创建Windows消息循环的原理,以为就可以搞定一切类似问题了…但是请看以下代码
DWORD CTestMFCDlg::ThreadFunc(PVOID yy) {
CAboutDlg dlg; dlg.DoModal();
return 0;
}
void CTestMFCDlg::OnOK() {
::CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)ThreadFunc,NULL,NULL,NULL);
}
在VC++6.0上编译运行出现以下ASSERT。
void CWnd::AssertValid() const {
……
CHandleMap* pMap = afxMapHWND(); …… CObject* p;
ASSERT((p = pMap->LookupPermanent(m_hWnd)) != NULL ||
(p = pMap->LookupTemporary(m_hWnd)) != NULL);
ASSERT((CWnd*)p == this); // must be us
MFC有一个全局的Hash表(通过afxMapHWND()获得),用于把HWND句柄与MFC的封装对象CWnd进行关联,这样就可以通过CWnd::FromHandle()等函数把CWnd对象Attach到一个已有的HWND句柄上,利用MFC的封装函数可以简化对HWND的直接操作。很显然,这里的Assert是因为CWnd对象根据自身的窗口句柄(m_hWnd)从Hash表里找到CWnd对象指针与对象的本身(this)并不相同!这说明,CWnd对象创建时注册到的Hash表与目前检索的Hash表并不是同一个。为什么会是这样的呢?
CHandleMap* PASCAL afxMapHWND(BOOL bCreate) {
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); if (pState->m_pmapHWND == NULL && bCreate) {
…….
pState->m_pmapHWND = new
CHandleMap(RUNTIME_CLASS(CTempWnd),offsetof(CWnd, m_hWnd)); ……
}
return pState->m_pmapHWND;
看来这个Hash表跟AfxGetModuleThreadState()有关,继续
AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState() {
return AfxGetModuleState()->m_thread.GetData();
}
AFX_MODULE_STATE* AFXAPI AfxGetModuleState() {
_AFX_THREAD_STATE* pState = _afxThreadState; AFX_MODULE_STATE* pResult;
if (pState->m_pModuleState != NULL) {
// thread state's module state serves as override pResult = pState->m_pModuleState;
} else {
// otherwise, use global app state pResult = _afxBaseModuleState.GetData();
}
ASSERT(pResult != NULL); return pResult;
}
那么_afxThreadState是什么呢?
EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)
class _AFX_THREAD_STATE : public CNoTrackObject { public:
_AFX_THREAD_STATE();
virtual ~_AFX_THREAD_STATE();
// override for m_pModuleState in _AFX_APP_STATE
AFX_MODULE_STATE* m_pModuleState; AFX_MODULE_STATE* m_pPrevModuleState;
#define THREAD_LOCAL(class_name, ident_name) \\
AFX_DATADEF CThreadLocal
#define EXTERN_THREAD_LOCAL(class_name, ident_name) \\
extern AFX_DATA THREAD_LOCAL(class_name, ident_name)
分析的结果是 extern CThreadLocal<_AFX_THREAD_STATE> _afxThreadState。
template
class CThreadLocal : public CThreadLocalObject {
AFX_INLINE TYPE* GetData() {
TYPE* pData = (TYPE*)CThreadLocalObject::GetData(&CreateObject); ASSERT(pData != NULL); return pData;
}
AFX_INLINE operator TYPE*()
{ return GetData(); }
AFX_INLINE TYPE* operator->()
{ return GetData(); }
static CNoTrackObject* AFXAPI CreateObject()
{ return new TYPE; }
可以看出来了,_afxThreadState 是一个全局的对象。通过该对象可以获得_AFX_THREAD_STATE对象,后者是线程相关的。CThreadLocalObject的代码不再分 析,大概就是检查当前的线程私有数据,如果有则返回,否则创建新的对象(即_AFX_THREAD_STATE)。
继续看AfxGetModuleState(),大致的意思是获取与当前线程相关联的AFX_MODULE_STATE对象,如果没有则获取该进程的缺省AFX_MODULE_STATE对象。
PROCESS_LOCAL(_AFX_BASE_MODULE_STATE, _afxBaseModuleState)
class _AFX_BASE_MODULE_STATE : public AFX_MODULE_STATE
#define PROCESS_LOCAL(class_name, ident_name) \\
AFX_DATADEF CProcessLocal
#define EXTERN_PROCESS_LOCAL(class_name, ident_name) \\
extern AFX_DATA PROCESS_LOCAL(class_name, ident_name)
继续看AfxGetModuleThreadState(),在获得了AFX_MODULE_STATE对象之后,访问其m_thread成员。这又是一个线程相关的数据,可以获得AFX_MODULE_STATE在不同线程中的私有数据。
// AFX_MODULE_STATE (global data for a module) class AFX_MODULE_STATE : public CNoTrackObject {
// define thread local portions of module state THREAD_LOCAL(AFX_MODULE_THREAD_STATE, m_thread)
现在简单总结一下,AfxGetModuleState()可以获得与执行线程关联的AFX_MODULE_STATE,而AfxGetModuleThreadState()可以获得与执行线程关联的AFX_MODULE_STATE与当前执行线程关联的AFX_MODULE_THREAD_STATE。看起来有点绕,再啰嗦几句。我们可以这样理解,每一个线程在执行的时候,需要以一个Module作为缺省上下文,比如查找资源的时候,默认从该Module里查找。这就是为什么我们需要在DLL的输出函数入口处进行资源切换,其实是把调用者线程的上下文设为当前代码所在的Module,从而保证资源的正确装载。另外,Module里代码需要根据不同的执行线程保存不同的全局数据(是不是为了避免访问的冲突?),于是Module还可以有自己独有的线程相关的数据。所以,AFX_MODULE_STATE可以在不同线程中使用,而AFX_MODULE_THREAD_STATE只能在特定线程中使用。
现在再来看afxMapHWND(),其返回的与工作线程关联的Module在工作线程下的Hash表。我们现在可以想象,如果工作线程关联Module的不同,或者相同关联Module下,而不是工作线程下,Hash表是完全不同的。
现在继续检查堆栈,发现是Assert来自CWnd的消息循环
int CWnd::RunModalLoop(DWORD dwFlags) {
……
if (!AfxGetThread()->PumpMessage())
CWinThread* AFXAPI AfxGetThread() {
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState(); CWinThread* pThread = pState->m_pCurrentWinThread;
// if no CWinThread for the module, then use the global app if (pThread == NULL)
pThread = AfxGetApp();
return pThread;
_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()
{ return afxCurrentWinApp; }
#define afxCurrentWinApp
AfxGetModuleState()->m_pCurrentWinApp
原来是获得当前执行线程相关的Module,并获得其包含/对应的CWinThread对象,并执行其消息循环函数。现在终于明白,为什么每个支持MFC的模块都要有一个CWinApp(从CWinThread派生)的全局对象了,原来是用于消息循环的!
请注意,如果当前线程没有相关联的Module,则返回当前进程的缺省AFX_MODULE_STATE对象,具体请参见AfxGetModuleState()。
好了,让我们来看一下,在消息循环里发生了什么。 BOOL CWinThread::PumpMessage() {
……
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur)) {
::TranslateMessage(&m_msgCur); ::DispatchMessage(&m_msgCur);
}
return TRUE;
BOOL CWinThread::PreTranslateMessage(MSG* pMsg) {
……
// walk from target to main window CWnd* pMainWnd = AfxGetMainWnd();
if (CWnd::WalkPreTranslateTree(pMainWnd->GetSafeHwnd(), pMsg))
return TRUE;
// in case of modeless dialogs, last chance route through main // window's accelerator table if (pMainWnd != NULL) {
CWnd* pWnd = CWnd::FromHandle(pMsg->hwnd); if (pWnd->GetTopLevelParent() != pMainWnd)
return pMainWnd->PreTranslateMessage(pMsg);
原来产生Assert的CWnd对象是主窗口对象,也就是CTestMFCDlg,而不是我们后面在工作线程里创建的CAboutDlg。这个问题让我们很疑惑,为什么在CAboutDlg的消息循环里会调用
正在阅读:
线程与消息06-09
湖北稻瘟病菌生理小种和水稻抗瘟性的鉴定及生化机制研究06-02
化工原理实验试题(cankaoyong)10-16
地基与基础评估报告05-16
罗威纳犬不能吃什么03-15
新版石材幕墙施工合同范本08-15
太原动物园导游词文档3篇04-19
软件工程硕士论文范文解析03-08
叶包子江财会计信息系统拼死拼活整理大全05-14
- 多层物业服务方案
- (审判实务)习惯法与少数民族地区民间纠纷解决问题(孙 潋)
- 人教版新课标六年级下册语文全册教案
- 词语打卡
- photoshop实习报告
- 钢结构设计原理综合测试2
- 2014年期末练习题
- 高中数学中的逆向思维解题方法探讨
- 名师原创 全国通用2014-2015学年高二寒假作业 政治(一)Word版
- 北航《建筑结构检测鉴定与加固》在线作业三
- XX县卫生监督所工程建设项目可行性研究报告
- 小学四年级观察作文经典评语
- 浅谈110KV变电站电气一次设计-程泉焱(1)
- 安全员考试题库
- 国家电网公司变电运维管理规定(试行)
- 义务教育课程标准稿征求意见提纲
- 教学秘书面试技巧
- 钢结构工程施工组织设计
- 水利工程概论论文
- 09届九年级数学第四次模拟试卷
- 线程
- 消息