友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

深入浅出MFC第2版(PDF格式)-第101部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!





     MltiThrd 。这一节,我要介绍MFC 多线程程序的写法。 



探索CWinThread 



     就像CWinApp 对象代表一个程序本身一样,CWinThread 对象代表一个执行线程本身。这 



     个MFC 类别我们曾经看过,第6章讲「MFC 程序的生死因果」时,讲到「CWinApp::Run 



     程序生命的活水源头」,曾经追踪过CWinApp::Run  的源头CWinThread::Run (里面有 



     一个消息循环)。可见程序的「执行事实」系发生在CWinThread 对象身上,而CWinThread 



     对象必须要(必然会)产生一个执行线程。 



     我希望「CWinThread 对象必须要(必然会)产生一个执行线程」这句话不会引起你的误会, 



     以为程序在application object (CWinApp 对象)的构造式必然有个动作最终调用到 



     CreateThread 或_beginthreadex 。不,不是这样。想想看,当你的Win32 程序执行起来, 



     你的程序并没有调用CreateProcess 为自己做出代表自己的那个进程,也没有调用 



     CreateThread 为自己做出代表自己的主执行线程(primary thread )的那个执行线程。为你的 



     程序产生第一个进程和执行线程,是系统加载器以及核心模块(KERNEL32 )合作的结果。 



     所以,再次循着第6章一步步剖析的步骤,MFC 程序的第一个动作是CWinApp::CWinApp 



      (比WinMain 还早),在那里没有「产生执行线程」的动作,而是已经开始在收集执行线程 



                                                                 755 


…………………………………………………………Page 818……………………………………………………………

                   第篇    深入  MFC  程式設計 



                    的相关信息了: 



                   // in MFC 4。2 APPCORE。CPP 

                   CWinApp::CWinApp(LPCTSTR lpszAppName) 

                    { 

                       。。。 

                       // initialize CWinThread state 

                       AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE(); 

                       AFX_MODULE_THREAD_STATE* pThreadState = pModuleState…》m_thread; 

                       ASSERT(AfxGetThread() == NULL); 

                       pThreadState…》m_pCurrentWinThread = this; 

                       ASSERT(AfxGetThread() == this); 

                       m_hThread = ::GetCurrentThread(); 

                       m_nThreadID = ::GetCurrentThreadId(); 

                       。。。 

                    } 



                  虽然MFC 程序只会有一个CWinApp 对象,而CWinApp 衍生自CWinThread,但并不 



                  是说一个MFC 程序只能有一个CWinThread 对象。每当你需要一个额外的执行线程,不 



                  应该在MFC 程序中直接调用::CreateThread 或_beginthreadex,应该先产生一个 



                  CWinThread 对象,再调用其成员函数CreateThread 或全域函数AfxBeginThread 将执行 



                  线程产生出来。当然, 现在你必然已经可以推测到,CWinThread::CreateThread 或 



                  AfxBeginThread  内部调用了::CreateThread 或_beginthreadex  (事实上答案是 



                  _beginthreadex )。 



                  这看起来颇有值得商议之处:为什么CWinThread 构造式不帮我们调用AfxBeginThread 



                  呢?似乎CWinThread 为德不卒。 



                   图14…5 就是CWinThread 的相关源代码。 



756 


…………………………………………………………Page 819……………………………………………………………

                                                              14       MFC  

                                                           第 章             多緒程式設計 



#0001  // in MFC 4。2 THRDCORE。CPP 

#0002  CWinThread::CWinThread(AFX_THREADPROC pfnThreadProc; LPVOID pParam) 

#0003  { 

#0004          m_pfnThreadProc = pfnThreadProc; 

#0005          m_pThreadParams = pParam; 

#0006 

#0007          monConstruct(); 

#0008  } 

#0009 

#0010  CWinThread::CWinThread() 

#0011  { 

#0012          m_pThreadParams = NULL; 

#0013          m_pfnThreadProc = NULL; 

#0014 

#0015          monConstruct(); 

#0016  } 

#0017 

#0018  void CWinThread::monConstruct() 

#0019  { 

#0020          m_pMainWnd = NULL; 

#0021          m_pActiveWnd = NULL; 

#0022 

#0023          // no HTHREAD until it is created 

#0024          m_hThread = NULL; 

#0025          m_nThreadID = 0; 

#0026 

#0027          // initialize message pump 

#0028  #ifdef _DEBUG 

#0029          m_nDisablePumpCount = 0; 

#0030  #endif 

#0031          m_msgCur。message = WM_NULL; 

#0032          m_nMsgLast = WM_NULL; 

#0033          ::GetCursorPos(&m_ptCursorLast); 

#0034 

#0035          // most threads are deleted when not needed 

#0036          m_bAutoDelete = TRUE; 

#0037 

#0038          // initialize OLE state 

#0039          m_pMessageFilter = NULL; 

#0040          m_lpfnOleTermOrFreeLib = NULL; 

#0041  } 

#0042 

#0043  CWinThread* AFXAPI AfxBeginThread (AFX_THREADPROC pfnThreadProc; LPVOID pParam; 

#0044          int nPriority; UINT nStackSize; DWORD dwCreateFlags; 

#0045          LPSECURITY_ATTRIBUTES lpSecurityAttrs) 

#0046  { 



                                                                                               757 


…………………………………………………………Page 820……………………………………………………………

                   第篇    深入  MFC  程式設計 



                    #0047      CWinThread* pThread = DEBUG_NEW CWinThread (pfnThreadProc; pParam); 

                    #0048 

                    #0049      if (!pThread…》CreateThread (dwCreateFlags|CREATE_SUSPENDED; nStackSize; 

                    #0050          lpSecurityAttrs)) 

                    #0051      { 

                    #0052          pThread…》Delete(); 

                    #0053          return NULL; 

                    #0054      } 

                    #0055      VERIFY(pThread…》SetThreadPriority(nPriority)); 

                    #0056      if (!(dwCreateFlags & CREATE_SUSPENDED)) 

                    #0057          VERIFY(pThread…》ResumeThread() != (DWORD)…1); 

                    #0058 

                    #0059      return pThread; 

                    #0060  } 

                    #0061 

                    #0062  CWinThread* AFXAPI AfxBeginThread (CRuntimeClass* pThreadClass; 

                    #0063          int nPriority; UINT nStackSize; DWORD dwCreateFlags; 

                    #0064          LPSECURITY_ATTRIBUTES lpSecurityAttrs) 

                    #0065  { 

                    #0066          ASSERT(pThreadClass != NULL); 

                    #0067          ASSERT(pThreadClass…》IsDerivedFrom(RUNTIME_CLASS(CWinThread))); 

                    #0068 

                    #0069          CWinThread* pThread = (CWinThread*)pThreadClass…》CreateObject(); 

                    #0070 

                    #0071          pThread…》m_pThreadParams = NULL; 

                    #0072          if (!pThread…》CreateThread (dwCreateFlags|CREATE_SUSPENDED; nStackSize; 

                    #0073                  lpSecurityAttrs)) 

                    #0074          { 

                    #0075                  pThread…》Delete(); 

                    #0076                  return NULL; 

                    #0077          } 

                    #0078          VERIFY(pThread…》SetThreadPriority(nPriority)); 

                    #0079          if (!(dwCreateFlags & CREATE_SUSPENDED)) 

                    #0080                  VERIFY(pThread…》ResumeThread() != (DWORD)…1); 

                    #0081 

                    #0082          return pThread; 

                    #0083  } 

                    #0084 

                    #0085  BOOL CWinThread::CreateThread (DWORD dwCreateFlags; UINT nStackSize; 

                    #0086          LPSECURITY_ATTRIBUTES lpSecurityAttrs) 

                    #0087  { 

                    #0088      // setup startup structure for thread initialization 

                    #0089      _AFX_THREAD_STARTUP startup; memset(&startup; 0; sizeof(startup)); 

                    #0090      startup。pThreadState = AfxGetThreadState(); 

                    #0091      startup。pThread = this; 

                    #0092      startup。hEvent = ::CreateEvent(NULL; TRUE; FALSE; NULL); 



758 


…………………………………………………………Page 821……………………………………………………………

                                                            14      MFC  

                                                          第 章          多緒程式設計 



       #0093      startup。hEvent2 = ::CreateEvent(NULL; TRUE; FALSE; NULL); 

       #0094      startup。dwCreateFlags = dwCreateFlags; 

       #0095      。。。 

       #0096      // create the thread (it may or may not start to run) 

       #0097      m_hThread = (HANDLE)_beginthreadex (lpSecurityAttrs; nStackSize; 

       #0098         &_AfxThreadEntry; &startup; dwCreateFlags | CREATE_SUSPENDED; (UINT*)&m_nThreadID); 

       #0099      。。。 

       #0100  } 



                            图14…5 CWinThread 的相关源代码 



       产生执行线程, 为什么不直接用::CreateThread 或_beginthreadex ? 为什么要透过 



       CWinThread 对象? 我想你可以轻易从MFC 源代码中看出, 因为 



       CWinThread::CreateThread 和AfxBeginThread  不只是::CreateThread 的一层包装,更做 



       了一些application framework 所需的内部数据初始化工作,并确保使用正确的C runtime 



       library 版本。源代码中有: 



       #ifndef _MT 



             。。。 // 做些设定工作,不产生执行线程,回返。 



       #else 



             。。。 // 真正产生执行线程,回返。 



       #endif //!_MT) 



       的动作,只是被我删去未列出而已。 



       接下来我要把worker thread 和UI thread  的产生步骤做个整理。它们都需要调用 



       AfxBeginThread  以产生一个CWinThread 对象,但如果要产生一个UI thread ,你还必须 



       先定义一个CWinThread 衍生类别。 



产生一个Worker Thread 



       Worker thread 不牵扯使用者接口。你应该为它准备一个执行线程函数, 然后调用 



       AfxBeginThread : 



                                                                                         759 


…………………………………………………………Page 822……………………………………………………………

                  第篇    深入  MFC  程式設計 



                     CWinThread            AfxBeginThread ThreadFunc 

                               * pThread =                (         ; &Param); 

                     。。。 

                     UINT ThreadFunc (LPVOID pParam) 

                     { 

                         。。。 

                     } 



                  AfxBeginThread  事实上一共可以接受六个参数,分别是: 



                     CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc; 

                                                       LPVOID pParam; 

                                                       int nPriority = THREAD_PRIORITY_NORMAL; 

                                                       UINT nStackSize = 0; 

                                                       DWORD dwCreateFlags = 0; 

                                                       LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); 



                  参数一pfnThreadProc 表示执行线程函数。参数二pParam 表示要传给执行线程函数的参 



                  数。参数三nPriority 表示优先权的微调值,预设为THREAD_PRIORITY_NORMAL ,也 



                  就是没有微调。参数四nStackSize 表示堆栈的大小,默认值0 则表示堆栈最大容量为 



                  1MB。参数五dwCreateFlags 如果为默认值0,就表示执行线程产生后立刻开始执行;如 



                  果其值为CREATE_SUSPENDED ,就表示执行线程产生后先暂停执行。之后你可以使用 



                  CWinThread::ResumeThread 重新执行它。参数六lpSecurityAttrs 代表新执行线程的安全防 



                  护属性。默认值NULL 表示此一属性与其产生者(也是个执行线程)的属性相同。 



                  在这里我们遭遇到一个困扰。执行线程函数是由系统调用的,也就是个callback 函数,不 



                  容许有this 指针参数。所以任何一般的C++ 类别成员函数都不能够拿来当做执行线程函 



                  式。它必须是个全域函数,或是个C++ 类别的static 成员函数。其原因我已经在第6 



                  章的「Callback 函数」一节中描述过了,而采用全域函数或是C++ static 成员函数,其 



                  间的优劣因素我也已经在该节讨论过。 



                  执行线程函数的类型AFX_THREADPROC  定义于AFXWIN。H 之中: 



                        // in AFXWIN。H 



                        typedef UINT (AFX_CDECL *AFX_THREADPROC)(LPVOID); 



                  所以你应该把本身的执行线程函数声明如下(其中的pParam  是个指针,在实用上可以指 



                  向程序员自定的数据结构): 



                        UINT ThreadFunc (LPVOID pParam); 



760 


…………………………………………………………Page 823……………………………………………………………

                                                         14      MFC  

                                                       第 章          多緒程式設計 



     否则,编译时会获得这样的错误消息: 



          error C2665: 'AfxBeginThread' : none of the 2 overloads can convert 

                                         parameter 1 from type 'void (unsigned long *)' 



      有时候我们会让不同的执行线程使用相同的执行线程函数,这时候你就得特别注意到执行线程 



      函数使用全域变量或静态变量时,数据共享所引发的严重性(有好有坏)。至于放置在 



      堆栈中的变量或对象,都不会有问题,因为每一个执行线程自有一个堆栈。 



产生一个UI Thread 



      UI thread 可不能够光由一个执行线程函数来代表,因为它要处理消息,它需要一个消息回 



      路。好得很,CWinThread::Run 里头就有一个消息循环。所以,我们应该先从CWinThread 



      衍生一个自己的类别,再调用AfxBeginThread  产生一个CWinThread 对象: 



      class CMyThread : public CWinThread 

      { 

          DECLARE_DYNCREATE(CMyThread) 



      public: 

          void BOOL InitInstance(); 

      }; 



      IMPLEMENT_DYNCREATE(CMyThread; CWinThread) 



      BOOL CMyThread::InitInstance() 

      { 

      。。。 

      } 



      CWinThread *pThread = AfxBeginThread(RUNTIME_CLASS(CMyThread)); 



      我想你对RUNTIME_CLASS 宏已经不陌生了,第3章和第8章都有这个宏的源代码 



      展现以及意义解释。AfxBeginThread  是上一小节同名函数的一个overloaded 函数,一共 



      可以接受五个参数,分别是: 



                                                                                     761 


…………………………………………………………Page 824……………………………………………………………

                   第篇    深入  MFC  程式設計 



                    CWinThread* AFXAPI AfxBeginThread(CRuntimeClass* pThreadClass; 

                                                     int nPriority = THREAD_PRIORITY_NORMAL; 

                                                     UINT nStackSize = 0; 

                                                     DWORD dwCreateFlags = 0; 

                                                     LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); 



                   最后四个参数的意义和默认值比上一节同名函数相同,但是少接受一个LPVOID pParam 



                   参数。 



                   你可以在AFXWIN。H  中找到CWinThread 的定义: 



                    class CWinThread : public CCmdTarget 

                    { 

                       DECLARE_DYNAMIC(CWinThread) 



                       BOOL CreateThread(D
返回目录 上一页 下一页 回到顶部 9 10
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!