MFC线程简述:
MFC中存在三种线程:
A、Ui线程:通过创建窗口得到,具备标准窗口的功能。
B、工作者线程:通过CreateThread函数直接创建,不具备消息队列。
C、带消息队列的工作者线程:通过继承CWinThread得到,具备消息队列。
A种和C种线程使用消息队列,对于使用者来说,这两种线程需要自己本身或其他线程向其发送消息,才进行相应工作,否则保持静默状态,该两种线程擅长实时处理外部信号。
B种线程不使用消息队列,可以用单个函数作为其线程的本体,适合处理步骤相对固定的算法。
线程间通信的方式:
方式一:全局变量,各线程通过修改全局变量传递信息,通过循环检测查看信息。
方式二:消息传递,通过windows消息机制传递。
方式一问题主要有两个:第一是全局变量过多,不便于管理;第二个是循环检测过于缓慢,并且较不灵活,不能够迅速响应变化。
因此,只有B类型,工作者线程适合使用方式一,其实现也相对简单,下面给出例子:
MyThread.h
class MyThread
{
public:
MyThread();
~MyThread();
void start(); //启动线程
void setThreadNumber(int); //设置线程标识号
protected:
static DWORD WINAPI run(LPVOID lpPram); //线程本体
private:
struct Data
{
int threadNum;
} data; //线程私有变量,每个线程都有独立的data
LPVOID lpData; //线程私有变量的指针
static int cnt; //该类型线程的一个静态(类似全局)变量,该类产生的线程共用
static HANDLE hMutexCnt; //静态变量cnt关联的互斥对象,用于协调各线程对cnt的访问。
};
MyThread.cpp
//注意静态变量需作为全局变量声明,如下
int MyThread::cnt = 100;
HANDLE MyThread::hMutexCnt = CreateMutex(NULL, FALSE, NULL);
//---------------- public ------------------
MyThread::MyThread()
{
this->lpData = (LPVOID)&this->data; //初始化私有变量的指针
}
MyThread::~MyThread()
{
}
void MyThread::start()
{
HANDLE hThread = CreateThread(NULL, 0, MyThread::run, this->lpData, 0, NULL);
CloseHandle(hThread); //注意此处并不是杀死线程,而是关闭其句柄(因为现在暂不需要),当线程自行结束后系统会释放句柄。
}
void MyThread::setThreadNumber(int threadNum)
{
this->data.threadNum = threadNum;
}
//--------------- protected -----------------
DWORD WINAPI MyThread::run(LPVOID lpPram)
{
MyThread::Data *tpData = (MyThread::Data *)lpPram; //注意与CreateThread处比较,lpPram其实就是this->lpData,之所以要传递指针是因为静态函数是该类所有对象的公有成员,不能通过this来指定成员变量。
cout << "Thread " << tpData->threadNum << " is running\n" << endl;
while (true) //循环
{
if (MyThread::cnt > 0)
{
WaitForSingleObject(MyThread::hMutexCnt, INFINITE); //检查是否有其他的
线程在使用cnt,如有,等待,无,则抢占cnt的使用权。
cout << "Thread " << tpData->threadNum << " : " << MyThread::cnt-- << endl;
ReleaseMutex(MyThread::hMutexCnt); //释放cnt的使用权。
}
else
{
WaitForSingleObject(MyThread::hMutexCnt, INFINITE);
cout << "None.\n" << endl;
ReleaseMutex(MyThread::hMutexCnt);
break; //当cnt减到为0的时候,退出循环。
}
}
return 0; //函数返回,线程结束,自行退出,并释放作用域内的所有变量占用的内存。}
在使用的地方:
MyThread thread1, thread2; //新建两个MyThread类型的线程
thread1.setThreadNumber(1); //设置线程标号
thread2.setThreadNumber(2);
thread1.start(); //启动线程,但不需要关闭,自行关闭。
thread2.start();
可以观察这两个线程是如何工作的。
需要注意的地方:工作者线程必须能自行退出,不推荐使用TerminateThread函数强行中止线程,因为这样会导致线程占用的内存无法释放。
方式二,分两种情况。
情况1:UI线程
在UI线程的h文件开头,添加以下代码:
#define WM_MY_MESSAGE (WM_USER+100)
定义WM_MY_MESSAGE的消息ID,+100是为了回避某些ActiveX控件占用的消息ID
在UI线程的h文件的类声明中,添加以下代码:
protected:
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
该函数作为MY_MESSAGE消息的响应函数,具有两个外部输入参数(指针)。
在UI线程的cpp处,在BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间,添加:
ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)
这样就将WM_MY_MESSAGE和OnMyMessage(WPARAM wParam, LPARAM lParam)连接起来了
定义函数OnMyMessage
LRESULT CThreadMsgDemoDlg::OnMyMessage(WPARAM wParam, LPARAM lParam)
{
MessageBox(L"WM_MY_MESSAGE");
return 0;
}
因此当WM_MY_MESSAGE被触发后,会弹出窗口显示"WM_MY_MESSAGE"
在主窗口添加一个按钮,在按钮的响应函数中:
::PostMessage(this->m_hWnd, WM_MY_MESSAGE, NULL, NULL);
第一个参数是目标的句柄,填入当前窗口的句柄;第二个参数是发送的消息,填入WM_MY_MESSAGE;第三四个参数是消息携带的数据,我们的消息不需要数据,因此是两个NULL
编译程序,点击按钮,若成功,将弹出WM_MY_MESSAGE。
由于这是消息机制,而不是变量或指针,所以,无论任何程序程序向该UI线程发送WM_MY_MESSAGE,那么该窗口都会响应。
情况2:带消息队列的工作者线程。
MyThread.h
#define WM_MY_THREAD_MESSAGE (WM_USER+101) //定义消息ID
class MyThread :
public CWinThread
{
DECLARE_DYNCREATE(MyThread) //使能动态创建,不需要懂其原理,该类进行线程化时必须有此声明。
public:
CWinThread *m_pThrd = NULL; //控制CWinThread线程的指针,用以获取句柄或发送消息。
MyThread();
~MyThread();
virtual BOOL InitInstance(); //CWinThread线程的Run用作消息循环,不能重载,故用该成员函数初始化自身数据。
virtual int ExitInstance(); //CWinThread线程用该函数处理线程结束后残余的数据。
void start();
void stop(); //停止线程的函数与普通工作者线程不一样
void SendMsg();
protected:
afx_msg void OnThreadMessage(WPARAM wParam, LPARAM lParam); //消息响应函数DECLARE_MESSAGE_MAP() //消息声明,表示该类具有消息机制
};
MyThread.cpp
#include "MyThread.h"
IMPLEMENT_DYNCREATE(MyThread, CWinThread) //呼应DECLARE_DYNCREATE
MyThread::MyThread()
{
}
MyThread::~MyThread()
{
this->stop();
}
//注意以下信息声明不是自动生成的,要手动输入
BEGIN_MESSAGE_MAP(MyThread, CWinThread)
ON_THREAD_MESSAGE(WM_MY_THREAD_MESSAGE, OnThreadMessage)
END_MESSAGE_MAP()
void MyThread::start()
{
this->m_pThrd = AfxBeginThread(RUNTIME_CLASS(MyThread)); //注意!
}
void MyThread::stop()
{
if (this->m_pThrd == NULL)
return; //该类线程响应速度比UI线程更快,因此比较捉摸不定,程序关闭时也许自己就先行退出了,一定要检测一下。
HANDLE hThrd = this->m_pThrd->m_hThread;
if (hThrd)
{
this->m_pThrd->PostThreadMessage(WM_QUIT, NULL, NULL); //注意,是这样关闭的!
WaitForSingleObject(this->m_pThrd->m_hThread, INFINITE); //等待线程结束}
this->m_pThrd = NULL;
}
BOOL MyThread::InitInstance()
{
// TODO: 在此添加专用代码和/或调用基类
return true; //注意这里!不要用CWinThread:: InitInstance()!!否则将作为普通线程启动!
}
int MyThread::ExitInstance()
{
// TODO: 在此添加专用代码和/或调用基类
return CWinThread::ExitInstance();
}
void MyThread::SendMsg()
{
if (this->m_pThrd)
{
this->m_pThrd->PostThreadMessage(WM_MY_THREAD_MESSAGE, NULL, NULL); //
发消息的方法与UI不一样!
}
}
void MyThread::OnThreadMessage(WPARAM wParam, LPARAM lParam)
{
::AfxMessageBox(L"WM_MY_THREAD_MESSAGE");
}