在标准的MFC中使用的线程API是“AfxBeginThread”。
AfxBeginThread有两种重载形式,因此在MFC中创建的线程就有两种类型:工作线程、界面线程。
顾名思义,工作线程就是专门处理一些复杂的算法或者后台处理;界面线程就是在进行界面操作时避免因为一个界面的操作而锁死其他界面,所以就可以为专门的界面开一个线程。
一、参数说明:
重载1:创建工作线程
CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,
DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
重载2:创建界面线程
CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,
int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0,
DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL);
两个重载函数中的主要区别就在于第1、2个参数,重载1的第1个参数是用户定义的线程函数,重载2的第1个参数是用户界面所对应的类。
其他参数
LPVOID pParam:用户定义线程函数所需的参数,这个参数是个指针,为了给函数传递多个参数,通常定义一个结构体,把函数需要的参数都定义在结构体中,在创建工作线程前给结构体赋值;
int nPriority:指定线程的优先级,如果为0,则与创建该线程的线程相同,缺省值THREAD_PRIORITY_NORMAL就是0;
UINT nStackSize:指定线程的堆栈大小,如果为0,则与创建该线程的线程相同;
DWORD dwCreateFlags:是一个创建标识,如果是CREATE_SUSPENDED,则以悬挂状态创建线程,在线程创建后线程挂起,如果为0,则线程在创建后立即执行。
LPSECURITY_ATTRIBUTES lpSecurityAttrs:表示线程的安全属性,通常为NULL,NT下有用。
二、线程创建实例
思路:1:定义线程函数;2:创建线程;3:结束线程
1. 工作线程实例
首先定义线程函数,在头文件中申明时一定要申明为static静态函数
// myclass.h文件中的申明
static UINT MfcThreadProc(LPVOID lpParam);
// myclass.cpp文件中的实现
UINT CMyClass::MfcThreadProc(LPVOID lpParam)
{
// 对指针进行强制转型,对指针所指内容进行解码。
MyDataStruct *lpObject = (MyDataStruct*)lpParam;
if (lpObject == NULL)
return - 1; //输入参数非法
//线程成功启动
while (1)
{
..//
}
return 0; //返回0则表示线程正确退出线程
}
然后创建线程
//参数的数据结构
typedef struct MyData
{
int a;
bool b;
double c[10];
}MyDataStruct;
//设置参数
MyDataStruct params;
params.a = 100;
params.b = false;
memset(params.c, 20.0, 10*sizeof(double));
//创建线程
CWinThread* m_pThread;
m_pThread= AfxBeginThread(MfcThreadProc,¶ms);
m_pThread ->m_bAutoDelete = TRUE;
最后结束线程
m_pThread->m_bAutoDelete = True;
表示在线程执行完后自动删除线程对象m_pThread。
另外,有时线程函数可能需要访问本类中的其他成员变量,这种情况下在创建线程时,线程函数的参数可以写this,然后在线程函数里将参数强制转换成类,就可以访问类中的成员变量和函数
//创建线程
…………
m_pThread= AfxBeginThread(MfcThreadProc,this);
…………
//线程函数
UINT CMyClass::MfcThreadProc(LPVOID lpParam)
{
// 对指针进行强制转型,对指针所指内容进行解码。
CMyClass *pMyClass = (CMyClass*)lpParam;
......
return 0; //返回0则表示线程正确退出线程
}
2. 界面线程实例
界面线程是由CWinThread派生类控制的,这个派生类和CWinAPP极为类似,实际上CWinAPP也是一个UI线程,他是应用程序的主线程,一般我们所说的界面线程,是指除主线程之外的界面线程。
1,创建用户界面线程时,必须首先从CWinThread派生类。
2,使用DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE以建立类表。
3,使用VS提供的增加MFC类向导可以轻松完成类的添加工作,类添加后需要对其进行必要的修改。
需要重写如下两个成员函数。
//执行线程实例初始化,必须重写
virtualBOOL InitInstance();
//线程终止时执行清除,通常需要重写
virtualint ExitInstance(); // default will 'delete this'
4,创建线程函数原型。
CWinThread *AfxBeginThread(
CRuntimeClass *pThreadClass, //从CWinThread派生的类的 RUNTIME_CLASS
int nPriority =THREAD_PRIORITY_NORMAL,
UINT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs= NULL);
5,启动线程
设置CWinThread类的m_pMainWnd成员,否则这个线程不会随着界面的关闭而退出。
CWinThread *pTread = AfxBeginThread(RUNTIME_CLASS(CmyUIClassName));
6,结束线程
①UI线程有消息队列,所以结束一个UI线程最好的方法是发一个WM_QUIT消息给消息队列。
PostQuitMessage()
PostThreadMessage()
但是发出消息后最好等待看UI线程是否已经退出(很重要)。
if(pThread)
{
// 1. 发一个WM_QUIT消息结束UI线程
pThread->PostThreadMessage(WM_QUIT, NULL, NULL);
// 2. 等待UI线程正常退出
if (WAIT_OBJECT_0 == WaitForSingleObject(pThread->m_hThread, INFINITE))
{
// 3. 删除 UI 线程对象,只有当你设置了m_bAutoDelete = FALSE; 时才调用
delete pThread;
}
}
②普通的工作线程收到返回值既表示正确退出。
另外,如果需要中途退出线程,可以用下面的方法检查
DWORD dwExitCode = 0;
GetExitCodeThread(hThreads, &dwExitCode);
if (dwExitCode == STILL_ACTIVE)
{
TerminateThread(hThreads,-1);
CloseHandle(hThreads[0]);
}