在Win32系统中有两种线程,第一种为用户界面(UI)线程,这种线程通常用于通过窗口接收用户输入,它有自己的消息循环,我们在MFC窗口应用中的主线程就是这种线程。第二种为工作线程,这种线程可以理解为一段不停工作的代码,用来处理一些长时间的操作,但是它没有消息循环。
工作线程是比较容易理解的,因为它一般被用来执行一些后台的操作。
下面是个简单的例子:
//工作线程
UINT WorkerThread( LPVOID pParam )
{
//接收一个窗口类指针,然后设置窗口标题
for(int i=0;i<100;i++)
{
//TRACE("thread %d
",i);
Sleep(100);
}
return 0; // 返回并退出线程
//或者调用void AfxEndThread( UINT nExitCode );来退出
}
//创建线程
void CSam_sp_43Dlg::OnWorkT()
{
AfxBeginThread(WorkerThread,NULL);
}
你会看到在工作线程运行时主线程依然在工作,两者间是并行运行的。
下面我们来看看另一种线程,UI线程。假设我们现在创建一个无模式的对话框,并且在主线程序中进行非常耗时的操作。你会看到对话框也停止了相应,在这里我们使用一个定时器来演示。
//创建无模式对话框
void CThreadDemoDlg::OnUdlg()
{
CMyDlg *pDlg=new CMyDlg;
pDlg->Create(IDD_GUIDLG);
pDlg->ShowWindow(SW_SHOW);
}
//进行长时间操作
void CThreadDemoDlg::OnLoop()
{
// TODO: Add your control notification handler code here
for(int i=0;i<100;i++)
{
int _x=0;
_x++;
Sleep(100);
}
}
//在对话框种使用TIMER来查看消息循环是否在继续
BOOL CMyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
SetTimer(1,500,NULL);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CMyDlg::OnTimer(UINT nIDEvent)
{
m_uTimer++;
m_szTimer.Format("timer is : %d",m_uTimer);
UpdateData(FALSE);//更新窗口显示
CDialog::OnTimer(nIDEvent);
}
这是因为在同一线程中消息循环只有一个,当其中一部分代码在长时间运行时消息循环就停止了,当然这可以通过引入二次消息循环(在长时间的操作中手工获取消息队列中的消息并分派)的方式来避免。
下面我们通过采用UI线程的方法来使对话框的消息循环处于另一个线程中。首先我们要以CWinThread为基类创建一个新类。
然后重载InitInstance成员函数。
BOOL CMyThread::InitInstance()
{
// TODO: perform and per-thread initialization here
CMyDlg *pDlg=new CMyDlg;
pDlg->Create(IDD_GUIDLG);
pDlg->ShowWindow(SW_SHOW);
m_pMainWnd = pDlg;
return TRUE;
}
在运行是,首先创建一个无模式对话框,然后再创建一个UI线程,在按下LOOP按钮后你会看到与主线程位于同一线程的无模式对话框中的TIMER停止了工作,而位于UI线程中的对话框TIMER依然在工作。
在CWinThread还可以进行重载的成员函数还包括:
ExitInstance:在线程退出时会被调用。
OnIdle:在线程空闲时被调用。
PreTranslateMessage:在进行消息分派时被调用,可以重载这个函数来进行消息的预处理。
下载示范代码 20K
此外MSDN中MTMDI示范也是演示UI线程的例子。