线程被创建以后,它就成了一个执行代码的独立实体。有的线程生命周期很长,例如主线程,它的生存期跟进程的生存期一样。它在进程创建时被创建,主线程的结束也导致进程的终止。有的线程则在完成了一项任务后马上退出。不管是哪个线程,其经历的状态都可以被分为表5.1所示的3种。
线程刚开始被创建时,必须做一些初始化工作,为的是产生应用程序的工作平台——窗口。这项工作是每个线程实例都得做的,所以称之为Initialize Instance。
初始化工作完成后,线程进入消息循环,不断地移除并处理消息队列的消息,响应用户的请求。这是线程的主要工作状态,称之为Running。
消息循环结束后,线程就要准备终止自己的执行了,这时称它所做的工作为Exit Instance。
在CWinThread类中这几个状态用代码表示如下。
class CWinThread : public CObject
{
// ……其他成员
public:
// 允许重载的函数(Overridables)
// 执行线程实例初始化
virtual BOOL InitInstance();
// 开始处理消息
virtual int Run();
virtual BOOL PreTranslateMessage(MSG* pMsg);
virtual BOOL PumpMessage();
virtual BOOL OnIdle(LONG lCount);
virtual BOOL IsIdleMessage(MSG* pMsg);
// 线程终止时执行清除
virtual int ExitInstance();
// 当前正在处理的消息
MSG m_msgCur;
};
m_msgCur成员记录着当前的CWinThread对象正在处理的消息。InitInstance、Run和ExitInstance函数是由框架程序负责调用的,用户如果想添加额外的代码只需重载它们就可以了。下面是它们的实现代码。
int CWinThread::Run()
{
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
for(;;)
{
while(bIdle && !::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE))
{
if(!OnIdle(lIdleCount++))
bIdle = FALSE;
}
do
{
if(!PumpMessage())
return ExitInstance();
if(IsIdleMessage(&m_msgCur))
{
bIdle = TRUE;
lIdleCount = 0;
}
}while(::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE));
}
ASSERT(FALSE);
}
// 在消息送给Windows的TranslateMessage和DispatchMessage之前进行消息过滤
BOOL CWinThread::PreTranslateMessage(MSG* pMsg)
{
return FALSE;
}
BOOL CWinThread::PumpMessage()
{
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))
return FALSE;
if(!PreTranslateMessage(&m_msgCur)) // 没有完成翻译
{
::TranslateMessage(&m_msgCur);
::DispatchMessage(&m_msgCur);
}
return TRUE;
}
BOOL CWinThread::OnIdle(LONG lCount)
{
return lCount < 0;
}
BOOL CWinThread::IsIdleMessage(MSG* pMsg)
{
return TRUE;
}
BOOL CWinThread::InitInstance()
{
return FALSE;
}
int CWinThread::ExitInstance()
{
int nResult = m_msgCur.lParam;
return nResult;
}
在进行消息循环时CWinThread类进行了消息空闲处理。当消息队列中没有消息时,框架程序自动调用虚函数OnIdle,允许用户利用线程空闲的时间一些工作。下面这行代码。
while(::PeekMessage(&m_msgCur, NULL, 0, 0, PM_NOREMOVE));
的意思是查看(而不移除)消息队列中是否还有等待处理的消息,如果有,PeekMessage函数返回TRUE,Run函数继续调用PumpMessage移除并处理一个消息;如果没有就做空闲处理,试图调用OnIdle函数。
在使用框架之前,用户必须首先创建应用程序实例。这个应用程序实例用来初始化整个类库框架,也用来维护一些全局变量信息,我们用一个CWinApp类来描述它。
// CWinApp类
class CWinApp : public CWinThread //_AFXWIN.H
{
DECLARE_DYNCREATE(CWinApp)
public:
CWinApp();
virtual ~CWinApp();
// 属性
// WinMain函数的四个参数
HINSTANCE m_hInstance;
HINSTANCE m_hPrevInstance;
LPTSTR m_lpCmdLine;
int m_nCmdShow;
// 帮助操作,通常在InitInstance函数中进行
public:
HCURSOR LoadCursor(UINT nIDResource) const;
HICON LoadIcon(UINT nIDResource) const;
// 虚函数
public:
virtual BOOL InitApplication();
virtual BOOL InitInstance();
virtual int ExitInstance();
virtual int Run();
};
__inline HCURSOR CWinApp::LoadCursor(UINT nIDResource) const
{ return::LoadCursor(AfxGetModuleState()->m_hCurrentResourceHandle, (LPCTSTR)nIDResource); }
__inline HICON CWinApp::LoadIcon(UINT nIDResource) const
{ return::LoadIcon(AfxGetModuleState()->m_hCurrentResourceHandle, (LPCTSTR)nIDResource); }
CWinApp* AfxGetApp();
__inline CWinApp* AfxGetApp()
{ return AfxGetModuleState()->m_pCurrentWinApp; }
BOOL AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow);
#endif // __AFXWIN_H__
将其实现代码放在名为APPCORE.CPP的文件中。
// APPCORE.CPP文件
#include "_afxwin.h"
CWinApp::CWinApp()
{
// 初始化CWinThread状态
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);
m_hThread = ::GetCurrentThread();
m_nThreadID = ::GetCurrentThreadId();
// 初始化CWinApp状态
ASSERT(pModuleState->m_pCurrentWinApp == NULL);
pModuleState->m_pCurrentWinApp = this;
ASSERT(AfxGetApp() == this);
// 直到进入WinMain函数之后再设置为运行状态
m_hInstance = NULL;
}
CWinApp::~CWinApp()
{
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
if(pModuleState->m_pCurrentWinApp == this)
pModuleState->m_pCurrentWinApp = NULL;
}
BOOL CWinApp::InitApplication()
{
return TRUE;
}
BOOL CWinApp::InitInstance()
{
return TRUE;
}
int CWinApp::Run()
{
return CWinThread::Run();
}
int CWinApp::ExitInstance()
{
return m_msgCur.wParam;
}
IMPLEMENT_DYNCREATE(CWinApp, CWinThread)
此框架规定,每个应用程序必须有一个全局的CWinApp对象,这样CWinApp类的构造函数就会在WinMain函数执行之前被调用。构造函数除了初始化CWinApp对象之外,还初始化了主线程的线程状态和当前模块的模块状态。
还剩几个虚函数需要讲述,其中InitApplication负责每个程序只做一次的动作,而InitInstance负责每个线程只做一次的动作。在使用的时候,要视情况去重载不同的虚函数。
现在该在类库框架中实现WinMain函数的执行代码了。为这个文件命名为WINMAIN.CPP。
// WINMAIN.CPP文件
#include "_afxwin.h"
int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// 类库框架内部的初始化
if(!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// 应用程序的全局初始化
if(pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// 主线程的初始化
if(!pThread->InitInstance())
{
nReturnCode = pThread->ExitInstance();
goto InitFailure;
}
// 开始与用户交互
nReturnCode = pThread->Run();
InitFailure:
return nReturnCode;
}
其中负责框架内部初始化的AfxWinInit函数的实现代码如下。
// APPINIT.CPP文件
#include "_afxwin.h"
BOOL AfxWinInit(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
// 设置实例句柄
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_hCurrentInstanceHandle = hInstance;
pModuleState->m_hCurrentResourceHandle = hInstance;
// 为这个应用程序填写初始化状态
CWinApp* pApp = AfxGetApp();
if(pApp != NULL)
{
pApp->m_hInstance = hInstance;
pApp->m_hPrevInstance = hInstance;
pApp->m_lpCmdLine = lpCmdLine;
pApp->m_nCmdShow = nCmdShow;
}
return TRUE;
}
使用类库框架之后,不需要再与WinMain函数见面了,只需从CWinApp类派生自己的应用程序类,重载InitApplication或者InitInstance函数。如果想让程序进入消息循环,就令InitInstance函数返回TRUE,否则返回FALSE。
本小节的例子是为了说明使用框架程序以后程序的结构,所有它没有什么功能,运行之后仅仅弹出一个MessageBox对话框。
首先创建一个空的Win32工程,工程名称为05UseFrame。然后按照3.1.5小节所示的方法设置工程支持多线程,再将COMMON文件夹下的文件添加到工程中(至少要添加所有的.CPP文件)。如果觉得类视图中显示的内容太多,可以在类视图中新建一个目录,把添加进来的类全部拖到这个目录下面。最后新建两个文件UseFrame.h和UseFrame.cpp,下面是UseFrame.h文件中的代码。
05UseFrame
//UseFrame.h
#include "_afxwin.h"
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
};
下面是UseFrame.cpp文件中的代码。
// UseFrame.cpp文件
#include "UseFrame.h"
CMyApp theApp; // 应用程序实例对象
// CMyApp类
BOOL CMyApp::InitInstance()
{
::MessageBox(NULL, "主线程开始执行!", "CMyApp::InitInstance", 0);
return FALSE; // 不要进入消息循环
}
int CMyApp::ExitInstance()
{
::MessageBox(NULL, "主线程将要退出!", "CMyApp::ExitInstance", MB_OK);
return 0;
}
简单来说,要使用这个类库框架,必须定义一个从CWinApp类派生的应用程序类,在这个类里重载InitInstance函数写程序初始化代码。还必须要保证程序中有惟一的应用程序实例对象,如本例中的theApp。
这个工作流程并不是为写控制台应用程序设计的,所以只有学完了下一章,才会觉得在InitInstance函数中添加代码并不比在WinMain函数中麻烦。