要知道MFC类一般不是线程安全的,这点要时刻牢记。
书中前两个小节简单说了一下进程和线程:
对于Windows平台,进程简而言之就是一个应用程序,线程实际上是处理器寄存器的一个映像。线程包括应用程序在哪儿执行代码和线程的本地变量等信息。进程可以有多个线程,共享该进程中的全局变量和资源,但是拥有各自的执行点和本地变量。
MFC里面用CWinThread对象表示所有线程。CWinApp恰好从CWinThread派生,代表主线程。
为了使用一个线程,需要从CWinThread派生出自己的类,然后重载InitInstance, ExitInstance,并通过AfxBeginThread来创建线程对象。
CWinThread中包含几个成员变量和函数,我们关心如下几个:
成员 | 定义 |
m_pMainWnd | 该线程的主窗口 |
m_pActiveWnd | 当前活动窗口 |
m_hThread, m_nThreadID | 线程句柄和ID |
InitInstance, ExitInstance | 重载提供线程的初始化和终结 |
PreTranslateMessage | 过滤消息,处理快捷键 |
还有不少,到时候参考帮助文档吧。
线程同步,看MFC不如看Windows SDK更清楚一些,MFC提供的如下几个同步对象类:CEvent, CMutex, CSemaphore, CCriticalSection等。
书中给出了一个MFC多线程的例子,我感兴趣的地方是每个DLL函数入口都要求使用AFX_MANAGE_STATE宏,MFC源代码中也有不少地方在使用。让我们仔细研究一下它。
典型用法为:AFX_MANAGE_STATE(AfxGetStaticModuleState());
该宏定义在afxstat_.h中(VC7):
#define AFX_MANAGE_STATE(p) AFX_MAINTAIN_STATE2 _ctlState(p); // 用 p 构造结构 |
注意:仅当 _AFXDLL被定义时才有。无 _AFXDLL定义时该宏什么也不做。
其中AFX_MAINTAIN_STATE2定义为一个结构:
struct AFX_MAINTAIN_STATE2 { explicit AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pModuleState); // 显式构造函数。 ~AFX_MAINTAIN_STATE2(); // 这里面一定还干着什么勾当,我们一会去看
protected: AFX_MODULE_STATE* m_pPrevModuleState; // 下面接着去看 _AFX_THREAD_STATE* m_pThreadState; }; |
先看看AfxGetStaticModuleState()函数:(实现于dllmodul.cpp)
static _AFX_DLL_MODULE_STATE afxModuleState; // 在_AFXDLL定义下生效 AFX_MODULE_STATE* AFXAPI AfxGetStaticModuleState() { AFX_MODULE_STATE* pModuleState = &afxModuleState; // 返回模块状态对象地址 return pModuleState; } |
_AFX_DLL_MODULE_STATE的定义如下:
class _AFX_DLL_MODULE_STATE : public AFX_MODULE_STATE // 派生于 AFX_MODULE_STATE { public: // AfxWndProcDllStatic 函数包装 AfxWndProc 调用,在前面调用 AFX_MANAGE_STATE _AFX_DLL_MODULE_STATE() : AFX_MODULE_STATE(TRUE, AfxWndProcDllStatic, _MFC_VER) {} }; LRESULT CALLBACK AfxWndProcDllStatic(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { AFX_MANAGE_STATE(&afxModuleState); // 哦,这里也。。。 return AfxWndProc(hWnd, nMsg, wParam, lParam); } |
下面是AFX_MODULE_STATE的定义,好复杂呀:
// AFX_MODULE_STATE (global data for a module) class AFX_MODULE_STATE : public CNoTrackObject { public: // 仔细观察这个构造函数的参数名字和类型 AFX_MODULE_STATE(BOOL bDLL, WNDPROC pfnAfxWndProc, DWORD dwVersion,BOOL bSystem = FALSE); ~AFX_MODULE_STATE();
CWinApp* m_pCurrentWinApp; // AfxGetApp() 返回就是它 HINSTANCE m_hCurrentInstanceHandle; // AfxGetInstanceHandle() 返回它 HINSTANCE m_hCurrentResourceHandle; // AfxGetResourceHandle() 返回它 LPCTSTR m_lpszCurrentAppName; // AfxGetAppName() 返回它 BYTE m_bDLL; // TRUE if module is a DLL, FALSE if it is an EXE BYTE m_bSystem; // TRUE if module is a "system" module, FALSE if not BYTE m_bReserved[2]; // padding WNDPROC m_pfnAfxWndProc; DWORD m_dwVersion; // version that module linked against // define thread local portions of module state CThreadLocal<AFX_MODULE_THREAD_STATE> m_thread; // 这是一个线程局部变量 // 这下面还有一大堆我们删减掉了。。。 }; |
注意:有些成员外面带有_AFXDLL宏定义才生效的我们删减掉了。
这时,我们应该知道前面的afxModuleState里面都有些什么东西啦。
现在我们明白了AfxGetStaticModuleState()返回了什么,让我们接着看AFX_MAINTAIN_STATE2的构造和析构函数:
AFX_MAINTAIN_STATE2::AFX_MAINTAIN_STATE2(AFX_MODULE_STATE* pNewState) { m_pThreadState = _afxThreadState; // ?好像是当前线程的状态 m_pPrevModuleState = m_pThreadState->m_pModuleState; // 保存原来的模块状态 m_pThreadState->m_pModuleState = pNewState; // 切换到新的模块 } |
很奇怪,没有找到AFX_MAINTAIN_STATE2的析构函数,只能拿AFX_MAINTAIN_STATE的析构函数来分析了:(估计差不多)
AFX_MAINTAIN_STATE::~AFX_MAINTAIN_STATE() { _AFX_THREAD_STATE* pState = _afxThreadState; pState->m_pModuleState = m_pPrevModuleState; // 恢复到原来保存的模块状态 } |
至此,我们应该能够理解定义AFX_MAINTAIN_STATE2 _ctlState的含义了,在定义处调用了构造函数,完成模块状态切换;在析构(一般是函数返回处)中切换回去。
[好像噢]每个线程(CWinThread派生的)都有自己的线程状态(_afxThreadState),而HWND->CWnd, HMENU->CMenu, HDC->CDC, HGDIOBJ->CGdiObject等对象映射表都是线程相关的,所以。。。在一个线程无法映射另一个线程的HWND, HMENU等对象的:)