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

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

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



                       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 



                                    全域变量X 



                    3                                    1 



                 执行线程B先完成其工作,设定了全域变量X,然后执行线程A才读取X。 



                        ?? 表示执行次序 

                        ?? 



               图  14…6a    race condition (good) 



764 


…………………………………………………………Page 827……………………………………………………………

                                                                        14     MFC  

                                                                     第 章         多緒程式設計 



                           执行线程A                                    执行线程B  

                                               Bad 



                                                                       2 



                                                全域变量X 



                              1                                        3 



                          执行线程A先执行并读取全域变量X,然后执行线程B才设定X。太迟了。 



                                   ?? 表示执行次序 

                                   ?? 



          ????           圖 14…6b    race condition  (bad ) 



NULL                                    执行线程A                              执行线程B 



NULL 



                         执行线程A在此等待 

                                                                                     执行线程B在此等待 

                         全域变量global1 

                                                                                     全域变量global2 

                         从NULL  改变为合                      global 1 

                         法的值                               NULL                      从NULL  改变为合 



                                                                                     法的值 

                                                           NULL 

                         执行线程A在此提供 

                                                                                     执行线程B在此提供 

                                                          global 2 

                         全域变量global2                                                 全域变量global1 



                         的值 

                                                                                     的值 



                                   图14…7 死结 (deadlock) 



                                                                                                 765 


…………………………………………………………Page 828……………………………………………………………

                  第篇    深入  MFC  程式設計 



                  要解决这些问题,必须有办法协调各个执行线程的执行次序,让某个线程等待某个线程。 



                  Windows 系统提供四种同步化机制,帮助程序进行这种工作: 



                    1。 Critical Section (关键区域) 



                    2。 Semaphore  (号志) 



                    3。 Event (事件) 



                    4。 Mutex  (Mutual Exclusive ,互斥器) 



                  MFC 也提供了四个对应的类别: 



                                                  CObject 

                                                   CObject 



                                           CSyncObject 

                                           CSyncObject 



                                              CCriticalSection 

                                               CCriticalSection 



                                                  CEvent 

                                                   CEvent 



                                                  CMutex 

                                                  CMutex 



                                                CSemaphore 

                                                CSemaphore 



                  一想到本书的厚度,我就打消介绍同步机制的念头了。你可以在许多Visual C++ 或MFC 



                  程序设计书籍中得到这个主题的知识。 



             MFC 多线程程序实例 



                  我将在此示范如何把第1章最后的一个Win32 多线程程序MltiThrd 改装为MFC 程序。 



                  我只示范主架构(与CWinThread、AfxBeginThread 、ThreadFunc 有关的部份),程序 



                  绘图部份留给您做练习。 



766 


…………………………………………………………Page 829……………………………………………………………

                                                           14      MFC  

                                                        第 章            多緒程式設計 



首先我利用MFC AppWizard 产生一个Mltithrd 项目,放在书附盘片的Mltithrd。14 子目 



录中,并接受MFC AppWizard  的所有预设选项。 



接下来我在resource。h  中加上一些定义,做为执行线程函数的参数,以便在绘图时能够把 



代表各执行线程的各个长方形涂上不同的颜色: 



 #define HIGHEST_THREAD            0x00 

 #define ABOVE_AVE_THREAD          0x3F 

 #define NORMAL_THREAD             0x7F 

 #define BELOW_AVE_THREAD          0xBF 

 #define LOWEST_THREAD             0xFF 



  然后我在Mltithrd。cpp  中加上一些全域变量(你也可以把它们放在CMltithrdApp 之 



  中。我只是为了图个方便): 



CMltithrdApp theApp; 

CWinThread* _pThread'5'; 

DWORD _ThreadArg'5' = { HIGHEST_THREAD;    // 0x00 

                            ABOVE_AVE_THREAD;  // 0x3F 

                            NORMAL_THREAD;     // 0x7F 

                            BELOW_AVE_THREAD;  // 0xBF 

                            LOWEST_THREAD      // 0xFF 

                          };    //用来调整四方形颜色 



然后在CMltithrdApp::InitInstance  函数最后面加上一些码: 



 // create 5 threads and suspend them 

 int i; 

 for (i= 0; i《 5; i++) 

 { 

                    AfxBeginThread CMltithrdView::ThreadFunc 

    _pThread'i' =                    (                            ; 

                                      &_ThreadArg'i'; 

                                      THREAD_PRIORITY_NORMAL; 

                                      0; 

                                     CREATE SUSPENDED 

                                            _           ; 

                                      NULL); 

 } 



                                                                                          767 


…………………………………………………………Page 830……………………………………………………………

                  第篇    深入  MFC  程式設計 



                  // setup relative priority of threads 

                  _pThread'0'…》SetThreadPriority (THREAD_PRIORITY_HIGHEST); 

                  _pThread'1'…》SetThreadPriority (THREAD_PRIORITY_ABOVE_NORMAL); 

                  _pThread'2'…》SetThreadPriority (THREAD_PRIORITY_NORMAL); 

                  _pThread'3'…》SetThreadPriority (THREAD_PRIORITY_BELOW_NORMAL); 

                  _pThread'4'…》SetThreadPriority (THREAD_PRIORITY_LOWEST); 



                  这样一来我就完成了五个worker threads  的产生,并且将其优先权做了…2~+2 范围之间 



                  的微调。 



                  接下来我应该设计执行线程函数。就如我在第1章已经说过,这个函数的五个执行线程可以 



                  使用同一个执行线程函数。本例中是设计为全域函数好呢?还是static 成员函数好?如果 



                  是后者,应该成为哪一个类别的成员函数好? 



                  为了「要在执行线程函数做窗口绘图动作」的考量,我把执行线程函数设计为CMltithrdView 



                  的一个static 成员函数,并遵循应有的函数类型: 



                  // in MltithrdView。h 

                  class CMltithrdView : public CView 

                  { 

                  。。。 

                  public: 

                          CMltithrdDoc* GetDocument(); 

                          static UINT ThreadFunc(LPVOID); 

                  。。。 

                  }; 



                  // in MltithrdView。cpp 

                  UINT CMltithrdView::ThreadFunc(LPVOID ThreadArg) 

                  { 

                      DWORD dwArg = *(DWORD*)ThreadArg; 



                      // 。。。在这里做如同第1章的MltitThrd 一样的绘图动作 

                      return 0; 

                  } 



                  好,到此为止,编译联结,获得的程序将在执行后产生五个执行线程,并全部冻结。以Process 



                  Viewer (Visual C++ 5。0 所附工具)观察之,证明它的确有六个执行线程(包括一个主执行 



                  线程以及我们所产生的另五个执行线程): 



768 


…………………………………………………………Page 831……………………………………………………………

                                          14      MFC  

                                        第 章        多緒程式設計 



接下来,留给你的操作是: 



  1。 利用资源编辑器为程序加上各菜单项目,如图1…9。 



  2。 设计上述菜单项目的命令处理例程。 



  3。 在执行线程函数ThreadFunc  内加上计算与绘图能力。并判断使用者选择何种延 



  迟方式,做出适当反应。 



                                                                 769 


…………………………………………………………Page 832……………………………………………………………

                第篇    深入  MFC  程式設計 



770 


…………………………………………………………Page 833……………………………………………………………

                                          15           AppWizard 

                                        第 章 定製個 



第15 章 



           定制一个 AppWizard 



 我们的Scribble 程序一路走来,大家可还记得它一开始并不是平地而起,而是由 



 AppWizard  以「程序代码产生器」的身份,自动为我们做出一个我所谓的「骨干程序」来? 



 Developer's Studio 提供了一个开放的AppWizard 接口。现在,我们可以轻易地扩充 



 AppWizard :从小规模的扩充,到几乎改头换面成为一种全新类型的程序代码产生器。 



 Developer's Studio 提供了许多种不同的项目类型,供你选择。当你选按Visual C++ 5。0 整 



 合环境中的【File/New 】命令项,并选择【Projects 】附页,便得到这样的对话窗画面: 



                                                                  771 


…………………………………………………………Page 834……………………………………………………………

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