八宝书库 > 文学其他电子书 > 深入浅出MFC第2版(PDF格式) >

第152部分

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

小说: 深入浅出MFC第2版(PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




                               * 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(DWORD dwCreateFlags = 0; UINT nStackSize = 0; 

                               LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL); 

                       。。。 

                       int GetThreadPriority(); 

                       BOOL SetThreadPriority(int nPriority); 

                       DWORD SuspendThread(); 

                       DWORD ResumeThread(); 

                       BOOL PostThreadMessage(UINT message; WPARAM wParam; LPARAM lParam); 

                       。。。 

                    }; 



                   其中有许多成员函数和图14…4 中的Win32 API  函数有关。在CWinThread 的成员函数 



                   中,有五个函数只是非常单纯的Win32 API  的包装而已,它们被定义于AFXWIN2。INL 



                   文件中: 



                    // in AFXWIN2。INL 

                    // CWinThread 

                   _AFXWIN_INLINE BOOL CWinThread::SetThreadPriority (int nPriority) 

                       { ASSERT(m_hThread != NULL); return  ::SetThreadPriority(m_hThread; nPriority); } 

                   _AFXWIN_INLINE int CWinThread::GetThreadPriority () 

                       { ASSERT(m_hThread != NULL); return ::GetThreadPriority (m_hThread); } 

                                           CWinThread  ResumeThread 

                   _AFXWIN_INLINE DWORD               ::              () 

                       { ASSERT(m_hThread != NULL); return ::ResumeThread (m_hThread); } 

                   _AFXWIN_INLINE DWORD CWinThread::SuspendThread () 

                       { ASSERT(m_hThread != NULL); return ::SuspendThread (m_hThread); } 

                   _AFXWIN_INLINE BOOL CWinThread::PostThreadMessage (UINT message; WPARAM wParam; LPARAM 

                    lParam) 

                       { ASSERT(m_hThread != NULL); return ::PostThreadMessage (m_nThreadID; message; wParam; 

                    lParam); } 



762 


…………………………………………………………Page 825……………………………………………………………

                                             14     MFC  

                                           第 章       多緒程式設計 



执行线程的结束 



     既然worker thread  的生命就是执行线程函数本身,函数一旦return ,执行线程也就结束了, 



     自然得很。或者执行线程函数也可以调用AfxEndThread ,结束一个执行线程。 



     UI 执行线程因为有消息循环的关系,必须在消息队列中放一个WM_QUIT,才能结束执行 



     线程。放置的方式和一般Win32 程序一样,调用::PostQuitMessage  即可办到。亦或者, 



     在执行线程的任何一个函数中调用AfxEndThread ,也可以结束执行线程。 



    AfxEndThread  其实也是个外包装,其内部调用_endthreadex,这个动作才真正把执行线程 



     结束掉。 



     别忘了,不论worker thread 或UI thread,都需要一个CWinThread 对象,当执行线程结 



     束,记得把该对象释放掉(利用delete )。 



执行线程与同步控制 



     看起来执行线程的诞生与结束,以及对它的优先权设定、冻结、重新激活,都很容易。但 



     是我必须警告你,多线程程序的设计成功关键并不在此。如果你的每一个执行线程都非常独 



     立,彼此没有干联,也就罢了。但如果许多个执行线程互有关联呢?有经验的人说多线程程 



     式设计有多复杂多困难,他们说的并不是执行线程本身,而是指执行线程与执行线程之间的同 



     步控制。 



     原因在于,没有人能够预期执行线程的被执行。在一个合作型多任务系统中(例如Windows 



     3。x),操作系统必须得到程序的允许才能够改变执行线程。但是在强制性多任务系统中(如 



     Win95 或WinNT ),控制权被排程器强制移转,也因此两个执行线程之间的执行次序变得 



     不可预期。这不可预期性造成了所谓的race conditions 。 



     假设你正在一个文件服务器中编辑一串电话号码。文件打开来内容如下: 



        Charley 572…7993 



        Graffie 573…3976 



        Dennis 571…4219 



                                                                  763 


…………………………………………………………Page 826……………………………………………………………

               第篇    深入  MFC  程式設計 



               现在你打算为Sue 加上一笔新资料。正当你输入Sue  电话号码的时候,另一个人也打 



               开文件并输入另一笔有关于Jason  的资料。最后你们两人也都做了存盘动作。谁的资料 



               会留下来?答案是比较晚存盘的那个人,而前一个人的输入会被覆盖掉。这两个人面临 



               的就是race condition 。 



               再举一个例子。你的程序产生两个执行线程,A和B。执行线程B的任务是设定全域变量X。 



               执行线程A则要去读取X。假设执行线程B先完成其工作,设定了X,然后执行线程A才执行, 



               读取X,这是一种好的情况,如图14…6a。但如果执行线程A先执行起来并读取全域变量 



               X,它会读到一个不适当的值,因为执行线程B还没有完成其工作并设定适当的X。如图 



               14…6b。这也是race condition 。 



               另一种执行线程所造成的可能问题是:死结(deadlock )。图14…7 可以说明这种情况。 



                         图14…6a race condition (good) 



                 执行线程A                                执行线程B  



                                   Good 



                    2 



       

返回目录 上一页 下一页 回到顶部 0 0

你可能喜欢的