多線程編程之二——MFC中的多線程開發

來源:互聯網
上載者:User

下載原始碼

五、MFC對多線程編程的支援

  MFC中有兩類線程,分別稱之為工作者線程和使用介面執行緒。二者的主要區別在於工作者線程沒有訊息迴圈,而使用介面執行緒有自己的訊息佇列和訊息迴圈。
  工作者線程沒有訊息機制,通常用來執行後台計算和維護任務,如冗長的計算過程,印表機的後台列印等。使用介面執行緒一般用於處理獨立於其他線程執行之外的使用者輸入,響應使用者及系統所產生的事件和訊息等。但對於Win32的API編程而言,這兩種線程是沒有區別的,它們都只需線程的啟動地址即可啟動線程來執行任務。
  在MFC中,一般用全域函數AfxBeginThread()來建立並初始化一個線程的運行,該函數有兩種重載形式,分別用於建立工作者線程和使用介面執行緒。兩種重載函數原型和參數分別說明如下:

(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,                      LPVOID pParam,                      nPriority=THREAD_PRIORITY_NORMAL,                      UINT nStackSize=0,                      DWORD dwCreateFlags=0,                      LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

PfnThreadProc:指向工作者線程的執行函數的指標,線程函數原型必須聲明如下:

UINT ExecutingFunction(LPVOID pParam);

請注意,ExecutingFunction()應返回一個UINT類型的值,用以指明該函數結束的原因。一般情況下,返回0表明執行成功。

  • pParam:傳遞給線程函數的一個32位參數,執行函數將用某種方式解釋該值。它可以是數值,或是指向一個結構的指標,甚至可以被忽略;
  • nPriority:線程的優先順序。如果為0,則線程與其父線程具有相同的優先順序;
  • nStackSize:線程為自己分配堆棧的大小,其單位為位元組。如果nStackSize被設為0,則線程的堆棧被設定成與父線程堆棧相同大小;
  • dwCreateFlags:如果為0,則線程在建立後立刻開始執行。如果為CREATE_SUSPEND,則線程在建立後立刻被掛起;
  • lpSecurityAttrs:線程的安全屬性指標,一般為NULL;
 (2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass,                      int nPriority=THREAD_PRIORITY_NORMAL,                      UINT nStackSize=0,                      DWORD dwCreateFlags=0,                      LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

  pThreadClass 是指向 CWinThread 的一個匯出類的運行時類對象的指標,該匯出類定義了被建立的使用介面執行緒的啟動、退出等;其它參數的意義同形式1。使用函數的這個原型產生的線程也有訊息機制,在以後的例子中我們將發現同主線程的機制幾乎一樣。

下面我們對CWinThread類的資料成員及常用函數進行簡要說明。

  • m_hThread:當前線程的控制代碼;
  • m_nThreadID:當前線程的ID;
  • m_pMainWnd:指嚮應用程式主視窗的指標
BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0,UINT nStackSize=0,LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);

  該函數中的dwCreateFlags、nStackSize、lpSecurityAttrs參數和API函數CreateThread中的對應參數有相同含義,該函數執行成功,返回非0值,否則返回0。
  一般情況下,調用AfxBeginThread()來一次性地建立並啟動一個線程,但是也可以通過兩步法來建立線程:首先建立CWinThread類的一個對象,然後調用該對象的成員函數CreateThread()來啟動該線程。

virtual BOOL CWinThread::InitInstance();

   重載該函數以控制使用介面執行緒執行個體的初始化。初始化成功則返回非0值,否則返回0。使用介面執行緒經常重載該函數,工作者線程一般不使用InitInstance()。

virtual int CWinThread::ExitInstance();

   線上程終結前重載該函數進行一些必要的清理工作。該函數返回線程的退出碼,0表示執行成功,非0值用來標識各種錯誤。同InitInstance()成員函數一樣,該函數也只適用於使用介面執行緒。

六、MFC多線程編程執行個體

  在Visual C++ 6.0編程環境中,我們既可以編寫C風格的32位Win32應用程式,也可以利用MFC類庫編寫C++風格的應用程式,二者各有其優缺點。基於Win32的應用程式執行代碼小巧,運行效率高,但要求程式員編寫的代碼較多,且需要管理系統提供給程式的所有資源;而基於MFC類庫的應用程式可以快速建立起應用程式,類庫為程式員提供了大量的封裝類,而且Developer Studio為程式員提供了一些工具來系統管理使用者來源程式,其缺點是類庫代碼很龐大。由於使用類庫所帶來的快速、簡捷和功能強大等優越性,因此除非有特殊的需要,否則Visual C++推薦使用MFC類庫進行程式開發。

我們知道,MFC中的線程分為兩種:使用介面執行緒和工作者線程。我們將分別舉例說明。

用 MFC 類庫編程實現工作者線程

常式5 MultiThread5

為了與Win32 API對照,我們使用MFC 類庫編程實現常式3 MultiThread3。

  1. 建立一個基於對話方塊的工程MultiThread5,在對話方塊IDD_MULTITHREAD5_DIALOG中加入一個編輯框IDC_MILLISECOND,一個按鈕IDC_START,標題為“開始” ,一個進度條IDC_PROGRESS1;
  2. 開啟ClassWizard,為編輯框IDC_MILLISECOND添加int型變數m_nMilliSecond,為進度條IDC_PROGRESS1添加CProgressCtrl型變數m_ctrlProgress;
  3. 在MultiThread5Dlg.h檔案中添加一個結構的定義:
    struct threadInfo{UINT nMilliSecond;CProgressCtrl* pctrlProgress;};

    線程函數的聲明:

    UINT ThreadFunc(LPVOID lpParam); 

    注意,二者應在類CMultiThread5Dlg的外部。

    在類CMultiThread5Dlg內部添加protected型變數:

    CWinThread* pThread; 
  4. 在MultiThread5Dlg.cpp檔案中進行如下操作: 定義公開變數:
    threadInfo Info; 

    雙擊按鈕IDC_START,添加相應訊息處理函數:

    void CMultiThread5Dlg::OnStart() {// TODO: Add your control notification handler code hereUpdateData(TRUE);Info.nMilliSecond=m_nMilliSecond;Info.pctrlProgress=&m_ctrlProgress;pThread=AfxBeginThread(ThreadFunc,&Info);}

    在函數BOOL CMultiThread3Dlg::OnInitDialog()中添加語句:

    {……// TODO: Add extra initialization herem_ctrlProgress.SetRange(0,99);m_nMilliSecond=10;UpdateData(FALSE);return TRUE;  // return TRUE  unless you set the focus to a control}

    添加線程處理函數:

    UINT ThreadFunc(LPVOID lpParam){threadInfo* pInfo=(threadInfo*)lpParam;for(int i=0;i<100;i++){int nTemp=pInfo->nMilliSecond;pInfo->pctrlProgress->SetPos(i);Sleep(nTemp);}return 0;}

用 MFC 類庫編程實現使用介面執行緒

建立使用介面執行緒的步驟:

  1. 使用ClassWizard建立類CWinThread的衍生類別(以CUIThread類為例)

    class CUIThread : public CWinThread{DECLARE_DYNCREATE(CUIThread)protected:CUIThread();           // protected constructor used by dynamic creation// Attributespublic:// Operationspublic:// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CUIThread)public:virtual BOOL InitInstance();virtual int ExitInstance();//}}AFX_VIRTUAL// Implementationprotected:virtual ~CUIThread();// Generated message map functions//{{AFX_MSG(CUIThread)// NOTE - the ClassWizard will add and remove member functions here.//}}AFX_MSGDECLARE_MESSAGE_MAP()};
  2. 重載函數InitInstance()和ExitInstance()。
    BOOL CUIThread::InitInstance(){CFrameWnd* wnd=new CFrameWnd;wnd->Create(NULL,"UI Thread Window");wnd->ShowWindow(SW_SHOW);wnd->UpdateWindow();m_pMainWnd=wnd;return TRUE;}

    建立新的使用介面執行緒

    void CUIThreadDlg::OnButton1() {CUIThread* pThread=new CUIThread();pThread->CreateThread();}

    請注意以下兩點:

    A、在UIThreadDlg.cpp的開頭加入語句:

    #include "UIThread.h"

    B、把UIThread.h中類CUIThread()的建構函式的特性由 protected 改為 public。

      使用介面執行緒的執行次序與應用程式主線程相同,首先調用使用介面執行緒類的InitInstance()函數,如果返回TRUE,繼續調用線程的Run()函數,該函數的作用是運行一個標準的訊息迴圈,並且當收到WM_QUIT訊息後中斷,在訊息迴圈過程中,Run()函數檢測到線程空閑時(沒有訊息),也將調用OnIdle()函數,最後Run()函數返回,MFC調用ExitInstance()函數清理資源。
      你可以建立一個沒有介面而有訊息迴圈的線程,例如:你可以從CWinThread派生一個新類,在InitInstance函數中完成某項任務並返回FALSE,這表示僅執行InitInstance函數中的任務而不執行訊息迴圈,你可以通過這種方法,完成一個工作者線程的功能。

常式6 MultiThread6

  1. 建立一個基於對話方塊的工程MultiThread6,在對話方塊IDD_MULTITHREAD6_DIALOG中加入一個按鈕IDC_UI_THREAD,標題為“使用介面執行緒”
  2. 右擊工程並選中“New Class…”為工程添加基類為CWinThread派生線程類CUIThread。
  3. 給工程添加新對話方塊IDD_UITHREADDLG,標題為“線程對話方塊”。
  4. 為對話方塊IDD_UITHREADDLG建立一個基於CDialog的類CUIThreadDlg。使用ClassWizard為CUIThreadDlg類添加WM_LBUTTONDOWN訊息的處理函數OnLButtonDown,如下:
    void CUIThreadDlg::OnLButtonDown(UINT nFlags, CPoint point) {AfxMessageBox("You Clicked The Left Button!");CDialog::OnLButtonDown(nFlags, point);}
  5. 在UIThread.h中添加
    #include "UIThreadDlg.h"

    並在CUIThread類中添加protected變數CUIThread m_dlg:

    class CUIThread : public CWinThread{DECLARE_DYNCREATE(CUIThread)protected:CUIThread();           // protected constructor used by dynamic creation// Attributespublic:// Operationspublic:// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CUIThread)public:virtual BOOL InitInstance();virtual int ExitInstance();//}}AFX_VIRTUAL// Implementationprotected:CUIThreadDlg m_dlg;virtual ~CUIThread();// Generated message map functions//{{AFX_MSG(CUIThread)// NOTE - the ClassWizard will add and remove member functions here.//}}AFX_MSGDECLARE_MESSAGE_MAP()};
  6. 分別重載InitInstance()函數和ExitInstance()函數:
    BOOL CUIThread::InitInstance(){m_dlg.Create(IDD_UITHREADDLG);m_dlg.ShowWindow(SW_SHOW);m_pMainWnd=&m_dlg;return TRUE;}int CUIThread::ExitInstance(){m_dlg.DestroyWindow();return CWinThread::ExitInstance();}
  7. 雙擊按鈕IDC_UI_THREAD,添加訊息響應函數:
    void CMultiThread6Dlg::OnUiThread() {CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread));}

    並在MultiThread6Dlg.cpp的開頭添加:

    #include "UIThread.h"

  好了,編譯並運行程式吧。每單擊一次“使用介面執行緒”按鈕,都會彈出一個線程對話方塊,在任何一個線程對話方塊內按下滑鼠左鍵,都會彈出一個訊息框。

( 未完待續)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.