MFC CWinThread 教程

在现代的软件开发中,多线程的使用变得日益普遍。尤其是在需要进行复杂计算或者需要保持用户界面响应性的情况下。虽然.NET框架提供了丰富的多线程支持,但在一些老旧的系统或者特定的应用场景中,仍然需要使用MFC(Microsoft Foundation Classes)来实现多线程。本文将介绍如何使用MFC中的CWinThread类来创建和管理UI线程,并实现UI线程与主线程之间的消息通信。

问题陈述

问题陈述 #1:

"创建一个UI线程,该线程每秒向主线程发送消息,消息内容是递增的计数器值。"

解决问题的步骤指南

在Visual Studio中,可以通过向导快速创建MFC类,而不需要手动编写代码。以下是创建一个派生自CWinThread的类CCountingThread的步骤:

  1. 在项目视图中右键点击项目名称,选择"添加" -> "类"。
  2. 在打开的对话框中,将新类命名为CCountingThread。

向导将生成以下框架代码:

class CCountingThread : public CWinThread { DECLARE_DYNCREATE(CCountingThread) protected: CCountingThread(); virtual ~CCountingThread(); public: virtual BOOL InitInstance(); virtual int ExitInstance(); protected: DECLARE_MESSAGE_MAP() };

接下来,需要在类中添加三个变量:

  • UINT_PTR m_uTimerID:用于跟踪SetTimer的ID。
  • int m_iCount:用于跟踪下一个递增的值。
  • CWnd* m_pParentWnd:用于指向主窗口。

为了使主对话框能够每秒更新一次新的值,可以使用WM_TIMER消息,并在每个定时器消息上,向主窗口发送新值。以下是处理WM_TIMER消息的函数:

void CCountingThread::OnTimer(WPARAM wParam, LPARAM lParam) { m_pParentWnd->PostMessageW(WM_USER+2, 0, ++m_iCount); }

为了使CCountingThread::OnTimer(...)函数能够处理WM_TIMER消息,需要在BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间添加以下代码:

ON_THREAD_MESSAGE(WM_TIMER, &CCountingThread::OnTimer)

接下来,需要在CCountingThread::InitInstance()中激活定时器,并在CCountingThread::ExitInstance()中停止定时器:

BOOL CCountingThread::InitInstance() { m_uTimerID = SetTimer(NULL, 2001, 1000, NULL); return TRUE; } int CCountingThread::ExitInstance() { KillTimer(NULL, m_uTimerID); return CWinThread::ExitInstance(); }

至此,已经完成了WinThread派生类的创建。接下来,需要设计主窗口UI,包括一个编辑框(用于显示来自线程的值)和两个按钮(用于启动和停止线程)。同时,需要在类中添加指向CCountingThread的指针。

以下是启动按钮处理程序的代码:

void CUserThread1Dlg::OnBnClickedStartThread() { if (m_pRunningThread == NULL) { m_pRunningThread = (CCountingThread*)AfxBeginThread( RUNTIME_CLASS(CCountingThread), 0, 0, CREATE_SUSPENDED, NULL); m_pRunningThread->m_pParentWnd = this; m_pRunningThread->ResumeThread(); } }

使用AfxBeginThread API以挂起模式创建UI线程,将CCountingThread的运行时类作为第一个参数,CREATE_SUSPENDED作为第四个参数。然后,将主窗口指针提供给m_pRunningThread->m_pParentWnd,并使用m_pRunningThread->ResumeThread()启动线程。

以下是停止UI线程的代码:

void CUserThread1Dlg::OnBnClickedStopThread() { if (m_pRunningThread != NULL) { m_pRunningThread->PostThreadMessageW(WM_QUIT, 0, 0); m_pRunningThread = NULL; } }

关闭UI线程的最佳方式是向线程发送WM_QUIT消息,这将使线程的message-pump退出。

UI线程每秒发送WM_USER+2消息,更新计数器值,因此需要在对话框类中添加一个函数来处理它。以下是相关代码:

LRESULT CUserThread1Dlg::OnCountingIncrease(WPARAM wParam, LPARAM lParam) { CString strText; strText.Format(_T("%d"), lParam); m_edtCounting.SetWindowTextW(strText); return LRESULT(0); }

在BEGIN_MESSAGE_MAP()中添加消息监听器:

ON_MESSAGE(WM_USER+2, &CUserThread1Dlg::OnCountingIncrease)

编译并运行应用程序,查看其工作情况。

双向通信

问题陈述 #2:

"添加一个重置计数器按钮,将计数器值重置为零。"

步骤指南

在主对话框中添加一个名为“重置计数器”的新按钮,并在代码中添加相应的处理程序。

以下是“重置计数器”按钮的OnClick处理程序代码:

void CUserThread1Dlg::OnBnClickedResetThreadcounter() { if (m_pRunningThread != NULL) { m_pRunningThread->PostThreadMessageW(WM_USER+1, 0, 0); } }

m_pRunningThread是CCountingThread类的对象,在点击“启动线程”按钮时创建。使用PostThreadMessageW,将消息发送给UI线程

接下来,在CCountingThread类中添加一个函数来处理WM_USER+1用户消息:

void CCountingThread::ResetCounter(WPARAM wParam, LPARAM lParam) { m_iCount = 0; }

在BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()之间添加以下消息监听器:

ON_THREAD_MESSAGE(WM_USER+1, &CCountingThread::ResetCounter)
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485