1. 程式人生 > >【轉】 MFC執行緒程式設計

【轉】 MFC執行緒程式設計

執行緒技術使不同的程式碼可以同時執行。當然,只有在多C P U的計算機上,多個執行緒才能夠真正地同時執行。然而,由於作業系統把C P U的時間分成很短的片段分配給每個執行緒,這樣給人的感覺好像是多個執行緒真的同時執行。
      執行緒的概念與1 6位環境中的多工有很大的不同。或許曾聽人們這樣講: Win32是一種搶佔式作業系統,而Windows 3.1 是一種協作式的多工環境。其關鍵區別在於:在搶佔式多工環境中,作業系統負責管理哪個執行緒在什麼時候執行。如果當執行緒1暫停執行時,執行緒2才有機會獲得CPU時間,我們說執行緒1是搶佔的。如果某個執行緒的程式碼陷入死迴圈,這並不可怕,作業系統仍會安排時間給其他執行緒。在Windows 3.1下,程式設計師必須保證應用程式能夠把控制權返還給Windows。如果這一步失敗,將導致整個操作環境鎖死,或許你已經有過這樣的痛苦經歷。只要稍微想想便會明白, 16位的Windows是如此脆弱,它依賴於應用程式的執行情況,並且不允許程式陷入死迴圈或無窮遞迴以及任何封閉狀態。這是因為所有的應用程式都必須協助Windows工作,這種工作型別被稱為協作式多工系統。
      在很多情況下,需要採用多執行緒技術進行程式設計。例如,常用的字處理軟體Word,當輸入文字的時候,Word同時進行拼寫和語法的檢驗,也就是將文件中的詞語與詞庫中的詞語進行比較,並對文件中的語句進行語法分析。這些操作都比較耗費時間,但是我們在使用Word的時候並沒有感覺到輸入過程有明顯的滯後現象。這裡Word就採用了多執行緒技術,其中一個執行緒接收輸入,另一個執行緒進行拼寫和語法的檢驗。
      而對於在VC下編寫多執行緒的程式有多種方法可以直接使用WINDOWS提供的API函式編寫,當然最為方便的還是使用MFC編寫,今天我們在這裡以幾個具體的例子來說明一下如何用MFC來編寫多執行緒程式。
      ~~一、使用者介面執行緒示例:
      在這個例子中我們要學會如何建立一個可以單獨執行的功能,且可以和應用程式同時執行的執行緒,而且該執行緒需要自己的使用者介面,也就是說使用者的操作和你程式的運算不會有干擾。例如在文件應用程式中的查詢和替換功能。在這個例子中我們需要使用框架中的AfxBegin Thread()函式來建立使用者介面執行緒。這將對執行緒具有完全控制權,我們將建立自己的CWinThread派生執行緒類。
      具體的步驟如下:
      ~1)建立新的執行緒類
      使用Class Wizard建立CWinThread派生執行緒類。例如建立無模式對話方塊的執行緒類,請參考程式清單—使用者介面執行緒類。在本例中建立無模式對話方塊而不是有模式對話方塊的原因是,允許訊息從主應用程式連續地轉發到執行緒。
      ~2)建立使用者介面執行緒
      為啟動執行緒可以使用如下程式碼:
      C WinThread *pThread = AfxBeginThread(RUNTIME_CLASS(CWzdThread));
      執行緒需要呼叫: : PostQuitMessage(arg)來終止,這裡的arg引數需要使用者自己定義。應用程式為了獲得arg的值,可以呼叫如下程式碼:
      int arg = pThread -> GetExitCodeThread();
      注意對於應用程式直接結束執行緒沒有推薦的方式。執行緒必須自己退出並允許將自身清除。使用者需要做的是建立Windows訊息來通知執行緒終止。執行緒通過呼叫::PostQuitMessage (arg)來處理訊息。
      ~3)注意:
      1、工作者執行緒傾向於瑣碎的處理,與它不同的是,使用者介面執行緒具有自己的介面而且實際上類似於執行其他應用程式。建立執行緒而不是其他應用程式的好處是執行緒可與應用程式共享程式空間,這樣可以簡化執行緒與應用程式共享資料的功能。

2、典型情況是使用者介面執行緒用於完成查詢和替換等功能,或者是其他不希望佔用主應用程式大量處理時間但是需要一個介面的功能或服務,或者使用者也可完全不考慮介面,將這種型別的執行緒用於視窗訊息伺服器作為一種傳遞其訊息的方式,以避免使自己因佔用處理時間過多而陷入困境。
      3、在時間要求嚴格的應用程式(例如實時應用程式)中,不希望因為工作者執行緒啟動而等待,這時可將工作者執行緒中的控制邏輯內建到使用者介面執行緒中並提前建立執行緒。當需要處理事務時,向用戶介面執行緒傳送訊息,此時使用者介面執行緒已經執行並且在等待指令。
        程式清單:
      #if !defined(AFX_WZDTHREAD_H__411AE4C2_E515_11D1_9B80_00AA003D8695__INCLUDED_)
      #define AFX_WZDTHREAD_H__411AE4C2_E5151_1D1_9B80_00AA003D8695_ _INCLUDED _
     
      #if _MSC_VER >= 1000
      #pragma once
      #endif
      #include "WzdDialog.h"
      class CWzdThread : public CinThread
      {
      DECLARE_DYNCREATE( CWzdThread )
      protected:
      CWzdThread();
      public :
      virtual BOOL InitInstance();
      virtual int ExitInstance();
      protected:
      virtual ~CWzdThread();
      DECLARE_MESSAGE_MAP()
      private:
      CWzdDialog m_dlg;
      } ;
      #include "stdafx.h"
      #include "wzd.h"
      #include "WzdThread.h"
      #ifdef _DEBUG
      #define new DEBUG_NEW
      #undef THIS_FILE
      static char THIS_FILE[] = __FILE__;
      # end if
      IMPLEMENT_DYNCREATE( CWzdThread, CWinThread )
      CWzdThread::CWzdThread()
      {}
      BOOL CWzdThread::InitInstance()
      {
      m_dlg.Create( IDD_WZD_DIALOG );
      m_dlg.Show Window( SW_SHOW );
      m_pMainWnd = &m_dlg;
      return TRUE;
      }
      int CWzdThread::ExitInstance()
      {
      m_dlg.DestroyWindow( );
      return CWinThread : : ExitInstance( );
      }
      BEGIN_MESSAGE_MAP( CWzdThread, CWinThread )
      END_MESSAGE_MAP()
      ~~二、執行緒間的資料共享示例:
      在本例中演示了在幾個執行緒之間進行程式資料共享和通訊,同時避免由於兩個執行緒同時訪問相同的資料而引發的衝突。在本例中使用了三種MFC類:CMutex、CSingleLock和CMultiLock來同步多個執行緒對一個數據類的同時訪問。

具體步驟:
      首先我們要先線上程中確定共享的資料類。在每個類定義中嵌入CMutex物件,如下所示:
      class CWzdData : public CObject
      {
      : : :
      CMutex m_mutex;
      : : :
      } ;
      如果資料類沒有訪問其資料的成員函式,這一步將新增它們。這些函式如下所示:
      void CWzdData::GetData( int *pInt,float *pFloat,DWORD *pWord )
      {
      *pInt = m_nInt;
      *pFloat = m_fFloat;
      *pWord = m_dwWord;
      }
      void CWzdData::SetData(int nInt,float fFloat,DWORD dwWord)
      {
      m_nInt = nInt;
      m_fFloat = fFloat;
      m_dwWord = dwWord;
      }
      在引用已嵌入CMutex變數的SetData()函式堆疊上建立CSingleLock類的例項。使用CSingleLock的Lock()函式避免在函式內部對資料多重訪問,如下所示:
      BOOL CWzdData::SetData( int nInt,float fFloat,DWORD dwWord)
      {
      CSingleLock slock(&m_mutex);
      if (slock.Lock( 1000)) // 時間以毫秒記,
      {
      m_nInt = nInt;
      m_fFloat = fFloat;
      m_dwWord = dwWord ;
      return TRUE;
      }
      return FALSE;
      這段程式碼需要注意的是如果其他的執行緒同時訪問這個資料, Lock()將立刻返回。否則, Lock()在指定的毫秒數內等待,直到超時並返回FALSE。如果在這個類中儲存的資料與其他類中儲存的資料相關,則在兩個類中嵌入CMutex變數,兩邊都用CMultiLock等待,如下所示:
      CMutex mutex[2];
      mutex[0] = &mutex1;
      mutex[1] = &mutex2;
      CMultiLock mlock( mutex,2 ); // where 2 is the number of mutexes
      if (mlock.Lock(1000))
      { }
      CreateMutex()函式的功能並不僅僅只是追蹤應用程式的例項。在該例項中只是簡單使用其中的部分功能。
      具體的程式實現程式碼如下:
      #ifndef WZDDATA _ H
      #define WZDDATA _ H
      #include
      class CWzdData : public CObject
      {
      public:
      DECLARE_SERIAL( CWzdData )
      CWzdData();
      BOOL GetData(int *pInt,float *pFloat,DWORD *pWord);
      BOOL SetData(int nInt,float fFloat,DWORD dwWord);

CMutex m_mutex;
      int m_nInt;
      float m_fFloat;
      DWORD m_dwWord ;
      } ;
      #endif
     
      #include "stdafx.h"
      #include "WzdData.h"
     
      IMPLEMENT_SERIAL( CWzdData, CObject, 0 )
      CWzdData::CWzdData()
      {
      m_nInt = 0;
      m_fFloat = 0.0f;
      m _ d w Word = 0;
      BOOL CWzdData::GetData( int *pInt,float *pFloat,DWORD *pWord )
      {
      CSingleLock slock( &m_mutex );
      if (slock.Lock(1000))
      {
      *pInt = m_nInt;
      *pFloat = m_fFloat;
      *pWord = m_dwWord;
      return TRUE;
      }
      return FALSE;