MFC架构之CWinThread类

 2023-09-10 阅读 20 评论 0

摘要:我们知道,Windows以事件驱动方式工作,每个WIN32应用程序都至少包含一个消息队列和一个消息泵。消息队列建立在操作系统提供的内存保留区中,消息泵不断搜寻消息队列,将取得的消息分发给应用程序的各个部分进行处理,这个过程叫做消息循环。基

 我们知道,Windows以事件驱动方式工作,每个WIN32应用程序都至少包含一个消息队列和一个消息泵。消息队列建立在操作系统提供的内存保留区中,消息泵不断搜寻消息队列,将取得的消息分发给应用程序的各个部分进行处理,这个过程叫做消息循环。基本消息循环如下:

[cpp] view plaincopy

  1. while(GetMessage(&msg,0,0,0))  
  2. {  
  3.       //转换消息参数  
  4.       TranslateMesssage(&msg);  
  5.       //分发消息  
  6.       DispatchMessage(&msg);  
  7. }  

 

      Windows以线程封装消息循环,封装消息循环的线程叫做用户界面线程,即UI线程。该线程可以创建并撤销窗口。此外还有一种线程叫做工作者线程,它是辅助UI线程工作的,它没有消息循环,不能处理系统事件和窗口消息,也不能关联主窗口。主线程和辅线程虽然享有共同的虚拟地址空间,但各自占用独立的CPU时间片,参与系统资源的竞争。所以,可以使用辅线程完成经常性的、耗费机时的数据处理工作(例如网络通信),减轻UI线程的负担,确保UI线程及时响应用户的窗口操作。根据需要,一个应用程序中也可以创建多个UI线程。
       CWinThread类是MFC用来封装线程的,包括UI线程和工作者线程。因此每个MFC程序至少使用一个CWinThread派生类。被MFC程序员熟知的CWinApp应用类就从这里派生。

下面介绍几个实用的CWinThread类成员函数。

1.虚函数InitInstance
  Windows允许同时运行一个应用程序的多个备份,又称为运行一个程序的多个实例。 InitInstance就是“初始化实例”的意思,可见,它是在实例创建时首先被调用的。应用程序总要重载这个虚函数,进行系统设置,创建运行环境。例如,主窗口一定要在InitInstance()中创建,因为该函数退出后就进入该线程的消息循环。

2.虚函数Run
  该函数提供UI线程的消息循环,即反复地提取消息,分发消息,直到收到WM_QUIT退出循环,线程随即结束。在循环中,如果当前没有收到消息,则调用空闲消息处理程序OnIdle() 。以下是该函数的完整定义。

 

[cpp] view plaincopy

  1. virtual int CWinThread::Run()  
  2. {  
  3.     ASSERT_VALID(this);  
  4.     //是否要做空闲处理  
  5.     BOOL bIdle = TRUE;  
  6.     //用户记录空闲处理已经连接执行的次数  
  7.     LONG lIdleCount = 0;  
  8.     //acquire and dispatch messages until a WM_QUIT message is received.  
  9.     //消息循环  
  10.     for (;;)  
  11.     {  
  12.         //如果空闲状态为真,且消息队列为空,则进行空闲处理  
  13.         while(bIdle && !::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE))  
  14.     {  
  15.         //PeekMessage()用于检查消息队列是否为空,但并不处理消息  
  16.         //调用空闲处理程序,如果返回零,说明空闲处理已全部完成  
  17.         if (!OnIdle(lIdleCount++))  
  18.         bIdle = FALSE;  
  19.     }  
  20.     //空闲处理循环结束,进入消息泵循环  
  21.     do  
  22.     {  
  23.         //调用消息泵,提取消息并分发消息  
  24.         //如果收到WM_QUIT消息,则退出消息循环  
  25.         if (!PumpMessage())  
  26.             return ExitInstance();  
  27.         //根据刚处理的消息类型,判断是否应该在没有消息到来时立即进行空闲处理  
  28.         if (IsIdleMessage(&m_msgCur))  
  29.         {  
  30.             bIdle = TRUE;  
  31.             //在重新进行空闲处理前,清空空闲处理的执行次数  
  32.             lIdleCount = 0;  
  33.         }  
  34.     } while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE));  
  35.     }  
  36.     ASSERT(FALSE); //不可能执行的语句  
  37. }  
  38.   
  39. /*------------- 消息泵函数PumpMessage() ----------------*/  
  40. //省略了调试信息的输出功能  
  41. BOOL CWinThread::PumpMessage()  
  42. {  
  43.     ASSERT_VALID(this);  
  44.     //取出消息队列中的第一个消息,直到取得消息,该函数才返回  
  45.     if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))  
  46.     {  
  47.         //收到WM_QUIT消息  
  48.         return FALSE;  
  49.     }  
  50.     //处理消息,但不处理WM_KICKIDLE  
  51.     if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))  
  52.     {  
  53.         //转换虚键消息到字符消息  
  54.         ::TranslateMessage(&m_msgCur);  
  55.         //分发消息  
  56.         ::DispatchMessage(&m_msgCur);  
  57.     }  
  58.     return TRUE;  
  59. }  

 

     阅读PumpMessage代码可知,消息泵并不处理WM_KICKIDLE消息,收到该消息后,直接返回。其实,WM_KICKIDLE消息被用来刺激空闲处理的执行,它作为一个空消息促使::GetMessage()返回。虽然Run()是虚拟函数,但很少被重载。

3.虚函数ExitInstance
  与InitInstance()相反,该函数是在退出消息循环时执行,一般被框架调用,做最后的清理工作。但如果调用InitInstance()失败,ExitInstance()也会被调用。可以重载ExitInstance(),为线程做相关的清理工作。不要在除重载的Run()函数外的地方调用它。如果将CWinThread成员变量m_bAutoDelete设为 TRUE,CWinThread::ExitInstance()会删除当前的CWinThread对象。所以,如果在堆栈中构造了UI线程对象,可以利用默认的ExitInstance()自动将它删除。

4.虚函数virtual BOOL PreTranslateMessage(MSG *pMsg );

在消息在TranslateMessage和DispatchMessage之前过滤消息。

5.虚函数virtual BOOL OnIdle(LONG lCount);

在空闲时处理。

 

CWinThread的使用

常见的启动线程函数有三个:  
  CreateThread(),   _beginThread(以及_beginThreadEx()),AfxBeginThread()  
  1和2是sdk函数,3是mfc函数  
  至于启动的是工作者线程还是UI线程,是由函数3的参数来决定的 ;

一个例子:

 

[cpp] view plaincopy

  1. class CUIThread : public CWinThread  
  2. {  
  3.     DECLARE_DYNCREATE(CUIThread)  
  4. protected:  
  5.      CUIThread();           // protected constructor used by dynamic creation  
  6.     // Attributes  
  7. public:  
  8.     // Operations  
  9. public:  
  10.     // Overrides  
  11.      // ClassWizard generated virtual function overrides  
  12.      //{{AFX_VIRTUAL(CUIThread)  
  13. public:  
  14.      virtual BOOL InitInstance();  
  15.      virtual int ExitInstance();  
  16.      //}}AFX_VIRTUAL  
  17.     // Implementation  
  18. protected:  
  19.      virtual ~CUIThread();  
  20.      // Generated message map functions  
  21.      //{{AFX_MSG(CUIThread)  
  22.       // NOTE - the ClassWizard will add and remove member functions here.  
  23.      //}}AFX_MSG  
  24.      DECLARE_MESSAGE_MAP()  
  25. };  
  26.   
  27. //重载函数InitInstance()和ExitInstance()  
  28. BOOL CUIThread::InitInstance()  
  29. {  
  30.      CFrameWnd* wnd=new CFrameWnd;  
  31.      wnd->Create(NULL,"UI Thread Window");  
  32.      wnd->ShowWindow(SW_SHOW);  
  33.      wnd->UpdateWindow();  
  34.      m_pMainWnd=wnd;  
  35.      return TRUE;  
  36. }  
  37.   
  38. //创建新的用户界面线程   
  39. void CUIThreadDlg::OnButton1()  
  40. {  
  41.      CUIThread* pThread=new CUIThread();  
  42.      pThread->CreateThread();  
  43. }  

 

 请注意以下两点:
1.在UIThreadDlg.cpp的开头加入语句: #include "UIThread.h"
2.把UIThread.h中类CUIThread()的构造函数的特性由 protected 改为 public。
  用户界面线程的执行次序与应用程序主线程相同,首先调用用户界面线程类的InitInstance()函数,如果返回TRUE,继续调用线程的 Run()函数,该函数的作用是运行一个标准的消息循环,并且当收到WM_QUIT消息后中断,在消息循环过程中,Run()函数检测到线程空闲时(没有消息),也将调用OnIdle()函数,最后Run()函数返回,MFC调用ExitInstance()函数清理资源。
  你可以创建一个没有界面而有消息循环的线程,例如:你可以从CWinThread派生一个新类,在InitInstance函数中完成某项任务并返回FALSE,这表示仅执行InitInstance函数中的任务而不执行消息循环,你可以通过这种方法,完成一个工作者线程的功能。

 

另一个例子:

[cpp] view plaincopy

  1. //.h 文件  
  2. #define WM_TEST    WM_USER + 1  
  3.   
  4. class CTestThread : public CWinThread  
  5. {  
  6.     DECLARE_DYNCREATE(CTestThread)  
  7. protected:  
  8.     CTestThread ();          
  9.     virtual ~CTestThread ();  
  10. public:  
  11.     virtual BOOL InitInstance();  
  12.     virtual int  ExitInstance();  
  13. protected:  
  14.     afx_msg void OnTest(WPARAM wParam,LPARAM lParam);  
  15.     DECLARE_MESSAGE_MAP()  
  16. };  
  17.   
  18. //.Cpp 文件  
  19. #include "stdafx.h"  
  20. #include "TestThread.h"  
  21.   
  22. IMPLEMENT_DYNCREATE(CTestThread, CWinThread)  
  23.   
  24. CTestThread::CTestThread()  
  25. {  
  26. }  
  27.   
  28. CTestThread::~CTestThread()  
  29. {  
  30. }  
  31.   
  32. BEGIN_MESSAGE_MAP(CTestThread, CWinThread)  
  33.     ON_THREAD_MESSAGE(WM_TEST,OnTest)  
  34. END_MESSAGE_MAP()  
  35.   
  36. BOOL CTestThread::InitInstance()  
  37. {  
  38.         return TRUE;  
  39. }  
  40.   
  41. int CTestThread::ExitInstance()  
  42. {  
  43.     return CWinThread::ExitInstance();  
  44. }  
  45.   
  46. void CTestThread::OnTest(WPARAM wParam,LPARAM lParam)  
  47. {  
  48.     AfxMessageBox("test");  
  49. }  
  50.   
  51. //调用的地方  
  52. CWinThread* m_pThrd;  
  53. //启动  
  54. m_pThrd = AfxBeginThread(RUNTIME_CLASS(CTestThread));  
  55. // 需要执行线程中的操作时  
  56. m_pThrd->PostThreadMessage(WM_TEST,NULL,NULL);  
  57.   
  58. // 结束线程  
  59. HANDLE hp=m_pThrd->m_hThread;  
  60. if (hp)   
  61. {  
  62.     if (WaitForSingleObject(hp, 1) != WAIT_OBJECT_0)  
  63.     {  
  64.         TerminateThread(hp,0);  
  65.     }  
  66.     CloseHandle(hp);  
  67. }  
  68.    

Fr:http://blog.csdn.net/flowshell/article/details/6008279

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/40613.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息