通常情况下,一个次级线程要为主线程完成某种特定类型的任务,这就隐含着表示在主线程和次级线程之间需要建立一个通信的通道。一般情况下,有下面的几种方法实现这种通信任务:使用全局变量(上一节的例子其实使用的就是这种方法)、使用事件对象、使用消息。
一、使用消息通信
在Windows程序设计中,应用程序的每一个线程都拥有自己的消息队列,甚至工作线程也不例外,这样一来,就使得线程之间利用消息来传递信息就变的非常简单。首先用户要定义一个用户消息,如下所示:#define WM_USERMSG WMUSER+100;在需要的时候,在一个线程中调用::PostMessage((HWND)param,WM_USERMSG,0,0)或CwinThread::PostThradMessage()来向另外一个线程发送这个消息,上述函数的四个参数分别是消息将要发送到的目的窗口的句柄、要发送的消息标志符、消息的参数WPARAM和LPARAM。下面的代码是对上节代码的修改,修改后的结果是在线程结束时显示一个对话框,提示线程结束:
UINT ThreadFunction(LPVOID pParam)
{
while(!bend)
{
Beep(100,100);
Sleep(1000);
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
////////WM_USERMSG消息的响应函数为OnThreadended(WPARAM wParam,
LPARAM lParam)
LONG CTestView::OnThreadended(WPARAM wParam,LPARAM lParam)
{
AfxMessageBox("Thread ended.");
Retrun 0;
}
上面的例子是工作者线程向用户界面线程发送消息,对于工作者线程,如果它的设计模式也是消息驱动的,那么调用者可以向它发送初始化、退出、执行某种特定的处理等消息,让它在后台完成。在控制函数中可以直接使用::GetMessage()这个SDK函数进行消息分检和处理,自己实现一个消息循环。GetMessage()函数在判断该线程的消息队列为空时,线程将系统分配给它的时间片让给其它线程,不无效的占用CPU的时间,如果消息队列不为空,就获取这个消息,判断这个消息的内容并进行相应的处理。
消息相应创建:
BEGIN_MESSAGE_MAP(CTestDlg, CDialog)
//{{AFX_MSG_MAP(CTestDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_USERMSG,OnSetCommand)
//}}AFX_MSG_MAP
ON_WM_DESTROY()
END_MESSAGE_MAP()
// Generated message map functions
//{{AFX_MSG(CTestDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
virtual void OnOK();
afx_msg LRESULT OnSetCommand(WPARAM wParam,LPARAM lParam);//
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
LRESULT CTestDlg::OnSetCommand(WPARAM wParam,LPARAM lParam)
{
MessageBox("HELLO");
return 0;
}
遇到的问题:一、ONOK()函数会在窗口销毁前销毁进程,所以无法进行消息的投递;改用Sleep()可解决问题
二、PostMessage()是投递消息,是异步的,不等待返回,因此与创建的线程相互干扰,因用SendMessage
三、必须传递窗口句柄给线程,可以通过GetSafeHwnd()获得
我的案例:
}
UINT ThreadFunction(LPVOID pParm)
{
::WaitForSingleObject(threadStart, INFINITE);
AfxMessageBox("Start...");
while (!band)
{
AfxMessageBox("WAIT_OBJECT_O");
Sleep(1000);
Beep(100, 100);
Sleep(1000);
DWORD dwRet = ::WaitForSingleObject(threadEnd, 0);
if (dwRet == WAIT_OBJECT_0)
{
AfxMessageBox("WAIT_OBJECT_O");
band = TRUE;
}
Sleep(10);
}
DWORD bRet = SendMessage((HWND)pParm, WM_USERMSG, 0, 0);
//BOOL dwRet = ::PostMessage((HWND)pParm, WM_USERMSG, 0, 0);
////MessageBox(
return 0;
}
void CTestDlg::OnOK()
{
// TODO: Add extra validation here
//HWND hwnd = GetSafeHwnd();
//DWORD dwThread;
//hThread = CreateThread(NULL, 0, ThreadFunction, hwnd, 0, &dwThread);
//DWORD dwRet = GetLastError();
///*dwRet = WaitForSingleObject(hThread, 0);
//if (dwRet == WAIT_OBJECT_0)
// MessageBox("1");
//else
// MessageBox("0");*/
//Sleep(10);
// HWND hNotifyWnd = GetSafeHwnd();
// BOOL bRet = ::SendMessage(hNotifyWnd, WM_USERMSG, 0, 0);
//CDialog::OnOK();
hwnd = GetSafeHwnd();
SetEvent(threadStart);
hThread = AfxBeginThread(ThreadFunction, hwnd);
hThread->m_bAutoDelete = FALSE;
Sleep(10);
}
LRESULT CTestDlg::OnSetCommand(WPARAM wParam,LPARAM lParam)
{
MessageBox("HELLO");
return 0;
}
void CTestDlg::OnDestroy()
{
BOOL bRet = SetEvent(threadEnd);
DWORD dwExitCode;
GetExitCodeThread(hThread->m_hThread,&dwExitCode);
if (dwExitCode == STILL_ACTIVE)
MessageBox("active");
DWORD dwRet = WaitForSingleObject(hThread, INFINITE);
if (dwRet == WAIT_TIMEOUT)
MessageBox("...");
//delete hThread;
Sleep(10);
// TODO: Add your message handler code here
(二)用事件对象实现通信
在线程之间传递信号进行通信比较复杂的方法是使用事件对象,用MFC的Cevent类的对象来表示。事件对象处于两种状态之一:有信号和无信号,线程可以监视处于有信号状态的事件,以便在适当的时候执行对事件的操作。上述例子代码修改如下:
////////////////////////////////////////////////////////////////////
Cevent threadStart ,threadEnd;
UINT ThreadFunction(LPVOID pParam)
{
::WaitForSingleObject(threadStart.m_hObject,INFINITE);
AfxMessageBox("Thread start.");
while(!bend)
{
Beep(100,100);
Sleep(1000);
Int result=::WaitforSingleObject(threadEnd.m_hObject,0);
//等待threadEnd事件有信号,无信号时线程在这里悬停
If(result==Wait_OBJECT_0)
Bend=TRUE;
}
::PostMessage(hWnd,WM_USERMSG,0,0);
return 0;
}
/////////////////////////////////////////////////////////////
Void CtestView::OninitialUpdate()
{
hWnd=GetSafeHwnd();
threadStart.SetEvent();//threadStart事件有信号
pThread=AfxBeginThread(ThreadFunction,hWnd);//启动线程
pThread->m_bAutoDelete=FALSE;
Cview::OnInitialUpdate();
}
////////////////////////////////////////////////////////////////
Void CtestView::OnDestroy()
{
threadEnd.SetEvent();
WaitForSingleObject(pThread->m_hThread,INFINITE);
delete pThread;
Cview::OnDestroy();
}
运行这个程序,当关闭程序时,才显示提示框,显示"Thread ended"。