MFC 支持多线程应用程序的开发,应用程序的每个线程都是一个 CWinThread 对象,MFC 将线程划分为两种类型:工作者线程(Worker Thread)和用户界面线程(User_interface Thread),这两种类型都基于 CWinThread。
如果线程需要执行后台计算而不需要与用户交互,那么该线程为工作者线程。工作者线程没有消息循环,不处理窗口消息,用于在后台执行任务。该类线程是最常用的类型。
如果要处理用户输入并响应由用户产生的事件和消息,那么应该创建一个用户界面线程,它是通过自己的消息泵获取从系统接收消息。主线程本身就是一个用户界面线程,这是因为 CWinApp 派生于 CWinThread。用户可从 CWinThread 派生出自己的类来实现用户界面线程。
创建线程主要有以下 3 种方法:
(1) Windows 的 API 函数 CreateThread;
(2) MFC 全局函数 AfxBeginThread;
(3) MFC 的 CWinThread 类的 CreateThread 成员函数。
以下就具体介绍这 3 种线程的创建方法。
CreateThread( )函数建立进程的一个新线程。该函数的原型为:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes ,
DWORD dwStackSize ,
LPTHREAD_START_ROUTINE lpStartAddress ,
LPVOID lpParameter ,
DWORD dwCreationFlags ,
LPDWORD lpThreadId);
其中参数含义如下。
(1) lpThreadAttributes:指向一个 SECURITY_ATTRIBUTES 结构,用于指定线程的安全属性。如果使用默认安全属性,则置为 NULL。
(2) dwStackSize:指定线程用于堆分配堆栈的大小。如果为 0,则堆栈大小默认为和该进程的主线程的堆栈大小相同。
(3) lpStartAddress:指向新线程执行代码的开始地址,通常为包含线程代码的线程函数名。
(4) lpParameter:指定传递给线程函数的 32 位参数值。
(5) dwCreationFlags:线程创建标志。如果为 CREATE_SUSPENDED,则该线程创建在挂起状态,直至调用 ResumeThread 函数后才运行;如果为 0,则创建后立即运行。
(6) lpThreadId:指向一个 32 位变量,用于接收该线程的标识符。
如果该函数调用成功,则返回新线程的句柄,否则返回 NULL。
使用 CreateThread( )函数创建线程实例。
// 线程主体函数 UNIT ThreadProc(LPVOID pParam) {// 线程的实际代码 return 0; } HANDLE hThread; DWORD dwThreadID; DWORD dwParam; hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadProc, wParam,0,&dwThreadID); if(hThread= =NULL) MessageBox(" 创建线程错误 ");
【例 10.2】 使用 AfxBeginThread 来创建工作者线程和用户界面线程。
// 线程函数 UNIT ThreadProc(LPVOID pParam) { // 线程代码 return 0; } // 在调用进程中创建工作者线程 CWinThread *pThread; DWORD dwParam; pThread=AfxBeginThread(ThreadProc,&dwParam); if(pThread==NULL) { MessageBox(" 创建错误 "); // 错误处理 } // 创建用户界面线程例 // 定义 CWinThread 线程派生类 class CTestThread:public CWinThread { // } // 在调用进程中创建用户界面线程 CTestThread *pTestThread; pTestThread=(CTestThread *)AfxBeginThread(RUNTIME_CLASS(CTestThread), THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
CWinThread 是 MFC 提供的线程对象类,包括创建、管理和删除的一系列成员变量和成员函数。CWinApp 是 CWinThread 的派生类。
CWinThread 类支持工作者线程和用户界面线程。可以将一个指向 CWinThread 派生类的 CRuntimeClass 的指针作为参数传递给 AfxBeginThread 函数以创建一个用户界面线程。
CWinThread 类的 CreateThread 成员函数创建一个在调用进程的地址空间中执行的线程。该函数原型为:
BOOL CreateThread(DWORD dwCreateFlags=0,UINT nStackSize=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);
若该函数成功创建了线程,返回非 0,否则返回 0。
【例 10.3】 使用 CWinThread 类来创建线程。
CWinThread thread; // 创建 CWinThread 对象 thread.m_bAutoDelete=FALSE; // 线程终止时不自动删除该对象 thread.m_pfnThreadProc=ThreadProc; // 设置线程函数 thread.m_pThreadParams=&dwParam; // 传递给线程函数的参数 thread.CreateThrerad(); // 创建线程,参数使用默认值。
使用 SuspendThread 和 ResumeThread 函数(Windows API 或 CWinThread 类成员函数),线程可以挂起或恢复另一个线程的运行。当线程处于挂起状态时,线程不会被调度运行。SuspendThread 函数将当前线程的挂起次数加 1。若该值的挂起次数大于 0,则该线程不运行。ResumeThread 函数将当前线程的挂起次数减 1。当该值为 0 时,线程恢复运行,否则线程仍处于挂起状态。如果将线程创建在挂起状态,那么在调用 ResumeThread 恢复执行之前可以完成对线程状态的初始化工作。
另外可通过调用 Sleep 或 SleepEx 函数暂时挂起当前线程一段指定时间。它常用于线程与用户的交互中,通过延迟执行线程足够长的时间让用户观察其结果。在睡眠期间线程不会被调度执行。
可以调用ExitThread 函数或TerminateThread 函数终止线程的执行。与进程的终止相似,一般情况下使用 ExitThread 函数来终止线程,只有在不得已的情况下才使用
TerminateThread 来终止线程。
可以调用全局函数 AfxEndThread 或者使用 return 语句来终止自己所在的线程。AfxEndThread 函数的原型为 AfxEndThread(UINT nExitCode),其中参数 nExitCode 为线程的退出码,这个值为 0,表示成功,如果为其他值,那么表示各种不同类型的错误。可以通过函数 GetExitCodeThread 来获取线程的退出码。