1. 程式人生 > >採用CreateThread()建立多執行緒程式 MFC

採用CreateThread()建立多執行緒程式 MFC

採用CreateThread()建立多執行緒程式

在window環境下,Win32 提供了一系列的API函式來完成執行緒的建立、掛起、恢復、終結以及通訊等工作:

1、主要的函式列表:

序號

函式名

功能

1

CreateThread()

建立一個新執行緒

2

ExitThread()

正常結束一個執行緒的執行

3

TerminateThead()

強制終止一個執行緒的執行

4

ResumeThread()

重啟一個執行緒

5

SuspendThread()

掛起一個執行緒

6

GetExiCodeThread()

得到一個執行緒的退出碼

7

GetThreadPriority()

得到一個執行緒的優先順序

8

SetThreadPriority()

設定一個執行緒的優先順序

9

CloseHandle()

關閉一個執行緒的控制代碼

10

CreateRemoteThread()

再另一個程序中建立一個新執行緒

11

PostThreadMessage()

傳送一條訊息給指定的執行緒

12

GetCurrentThread()

得到當前的執行緒控制代碼

13

GetCurrentThreadId()

得到當前執行緒的ID

14

GetThreadId()

得到指定執行緒的ID

15

WaitForSingleObject()

等待單個物件

16

WaitForMultipleObjects()

等待多個物件

關於多執行緒的API函式還有很多,以上只是列出了一些比較常用的函式,欲知更多函式和函式的使用方法,請參考MSDN或網路資源,在此就不再介紹了。

2、執行緒函式的定義:

執行緒函式的規範格式定義為

DWORD  WINAPI ThreadProc (LPVOID lpParam);//格式不正確將無法呼叫成功。函式名稱沒有限制,只要符合命名規則就可以。

但我常常看到有下列的執行緒函式定義:

void ThreadProc ();//該格式也是可以的,但使用的時候要這樣通過

LPTHREAD_START_ROUTINE轉換,如:

(LPTHREAD_START_ROUTINE)ThreadProc

我建議還是使用規範的格式比較好,不推薦使用void ThreadProc ()格式。不信就請看看MSDN的說明吧:

Do not declare this callback function with avoid return typeand cast the function pointer to LPTHREAD_START_ROUTINE when creatingthe thread. Code that does this is common, but it can crash on 64-bit Windows.

而且執行緒函式必須是全域性函式,不能在類中宣告和定義。

3、多執行緒例項1:

我在此將寫一個簡單的多執行緒程式,用以展示多執行緒的功能和使用方法。該程式的主要的思想是畫3個進度條,分別以多執行緒和單執行緒方式完成,大家可以比較一下。

說明:

(1)該程式還將和單執行緒做對比。

(2)由於給執行緒的函式傳遞了多個引數,所以採用結構體的方式傳遞引數。

(3)為了演示效果,採用了比較耗時的打點處理。

主要的函式如下:

在標頭檔案的定義

[cpp] view plain copy print?
  1. //執行緒函式宣告
  2. DWORD WINAPI ThreadProc(LPVOIDlpParam);  
  3. //為了傳遞多個引數,我採用結構體
  4. struct threadInfo  
  5. {  
  6.     HWND hWnd;       //視窗控制代碼
  7.     int  nOffset;    //偏移量
  8.     COLORREF clrRGB; //顏色
  9. };  
  10. protected:  
  11. HANDLE hThead[3];    //用於儲存執行緒控制代碼
  12.     DWORD  dwThreadID[3];//用於儲存執行緒的ID
  13.   threadInfo Info[3];   //傳遞給執行緒處理函式的引數


//實現檔案中

[cpp] view plain copy print?
  1. //單執行緒測試
  2. void CMultiThread_1Dlg::OnBnClickedButton1()  
  3. {  
  4.     // TODO: 在此新增控制元件通知處理程式程式碼
  5.     //使能按鈕
  6.     GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);  
  7.     GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);  
  8.     CDC *dc = GetDC();  
  9.     CRect rt;  
  10.     GetClientRect(rt);  
  11.     dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//重新整理背景
  12.     dc->TextOut(97,470,"#1");  
  13.     dc->TextOut(297,470,"#2");  
  14.     dc->TextOut(497,470,"#3");  
  15.     //#1
  16.     for (int i=0;i<460;i++)  
  17.     {  
  18.        for (int j =10 ;j<200;j++)  
  19.        {  
  20.            dc->SetPixel(j,460-i,RGB(255,0,0));  
  21.        }  
  22.     }  
  23.     //#2
  24.     for (int i=0;i<460;i++)  
  25.     {  
  26.        for (int j =210 ;j<400;j++)  
  27.         {  
  28.            dc->SetPixel(j,460-i,RGB(0,255,0));  
  29.        }  
  30.     }  
  31.     //#3
  32.     for (int i=0;i<460;i++)  
  33.     {  
  34.        for (int j =410 ;j<600;j++)  
  35.        {  
  36.            dc->SetPixel(j,460-i,RGB(0,0,255));  
  37.        }  
  38.     }  
  39.     ReleaseDC(dc);  
  40.     //使能按鈕
  41.     GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);  
  42.     GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);  
  43. }  
  44. //多執行緒測試
  45. void CMultiThread_1Dlg::OnBnClickedButton2()  
  46. {  
  47.     // TODO: 在此新增控制元件通知處理程式程式碼
  48.     CDC *dc = GetDC();  
  49.     CRect rt;  
  50.     GetClientRect(rt);  
  51.     dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//重新整理背景
  52.     dc->TextOut(97,470,"#1");  
  53.     dc->TextOut(297,470,"#2");  
  54.     dc->TextOut(497,470,"#3");  
  55.     //初始化執行緒的引數
  56.     Info[0].hWnd = Info[1].hWnd = Info[2].hWnd = GetSafeHwnd();  
  57.     Info[0].nOffset = 10;Info[1].nOffset = 210;Info[2].nOffset = 410;  
  58.     Info[0].clrRGB = RGB(255,0,0);Info[1].clrRGB= RGB(0,255,0);Info[2].clrRGB = RGB(0,0,255);  
  59.     //建立執行緒
  60.     for (int i = 0;i<3;i++)  
  61.     {  
  62.        hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);  
  63.     }  
  64.     ReleaseDC(dc);  
  65. }  
  66. DWORD WINAPI ThreadProc(LPVOIDlpParam)  
  67. {  
  68.     threadInfo*Info = (threadInfo*)lpParam;  
  69.     CDC *dc = CWnd::FromHandle(Info->hWnd)->GetDC();  
  70.     for (int i=0;i<460;i++)  
  71.     {  
  72.        for (int j=Info->nOffset;j<Info->nOffset+190;j++)  
  73.        {  
  74.            dc->SetPixel(j,460-i,Info->clrRGB);  
  75.        }  
  76.     }  
  77.     DeleteObject(dc);  
  78.     return 0;  
  79. }  


執行效果:

單執行緒測試


多執行緒測試

工程原始碼下載地址:

歡迎大家修改和指正。

注意事項:

(1)傳遞給執行緒執行函式的引數不能是區域性變數,而且必須是引數的地址。如:

Int nOffset = 10;

CreateThread(NULL,0,ThreadProc,nOffset,0,&dwThreadID[i]);//錯誤

CreateThread(NULL,0,ThreadProc,&nOffset,0,&dwThreadID[i]);//錯誤

Int *pOffset = newint(10);

CreateThread(NULL,0,ThreadProc,pOffset,0,&dwThreadID[i]);//正確

(2)執行緒執行函式必須是全域性函式。

(3)請大家改改下面的程式,且解釋下為什麼?

這是我開始寫程式遇到的一個問題,

改寫上面的函式:只是將結構體中一個引數改為CDC指標,以便直接呼叫。

structthreadInfo

{

    CDC * dc;        //畫布

    int nOffset;    //偏移量

    COLORREFclrRGB; //顏色

};

//多執行緒測試

voidCMultiThread_1Dlg::OnBnClickedButton2()

{

    // TODO:在此新增控制元件通知處理程式程式碼

    CDC *dc =GetDC();

    CRectrt;

    GetClientRect(rt);

    dc->FillSolidRect(0,0,rt.Width(),rt.Height()-70,RGB(240,240,240));//重新整理背景

    dc->TextOut(97,470,"#1");

    dc->TextOut(297,470,"#2");

    dc->TextOut(497,470,"#3");

    //初始化執行緒的引數

    Info[0].dc = Info[1]dc = Info[2].dc = dc;

    Info[0].nOffset = 10;Info[1].nOffset = 210;Info[2].nOffset = 410;

    Info[0].clrRGB =RGB(255,0,0);Info[1].clrRGB=RGB(0,255,0);Info[2].clrRGB =RGB(0,0,255);

    //建立執行緒

    for (inti = 0;i<3;i++)

    {

       hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);

    }

    //ReleaseDC(dc);

}

//執行緒執行函式

DWORDWINAPI ThreadProc(LPVOIDlpParam)

{

    threadInfo*Info = (threadInfo*)lpParam;

    for (inti=0;i<460;i++)

    {

       for (intj=Info->nOffset;j<Info->nOffset+190;j++)

       {

           Info->dc->SetPixel(j,460-i,Info->clrRGB);

       }

    }

    return 0;

}

執行結果:

為什麼會這樣呢?我還沒有找到答案,望大家能給我個解釋,謝謝。

===========================================================

4、多執行緒例項2:

該例項將演示一個簡單的多執行緒協同工作的例子,以供大家學習和參考。大致原理是:5個人開始比賽(比如賽跑),誰先完成比賽就結束,並統比賽時間和贏者。主執行緒用於介面的相關顯示,5個執行緒模擬5個人的行為(賽跑),另外一個執行緒用於檢測5個執行緒的執行情況,只要有人到達終點,比賽就結束並做相關的技術統計。

主要函式為:

MulitThread_2Dlg.h : 標頭檔案

//宣告執行緒處理函式

DWORDWINAPI ThreadProc1(LPVOIDlpParam);

DWORDWINAPI ThreadProc2(LPVOIDlpParam);

//為了傳遞多個引數,我採用結構體

structthreadInfo1

{

    HWNDhWnd;       //視窗控制代碼

    int nOffset;    //偏移量

};

structthreadInfo2

{

    HWNDhWnd;          //視窗控制代碼

    HANDLE *phHandle;   //偏移量

};

protected:

    long  m_nTime;//時間

    HANDLEm_hThead[5];    //用於儲存執行緒控制代碼

    HANDLEhThead;     //用於儲存執行緒控制代碼

    DWORD m_dwThreadID[5];//用於儲存執行緒的ID

    threadInfo1Info1[5];  //傳遞給執行緒處理函式的引數

    threadInfo2Info2;

// MulitThread_2Dlg.cpp : 實現檔案

//更新時間:毫秒

voidCMulitThread_2Dlg::OnTimer(UINT_PTRnIDEvent)

{

    // TODO:在此新增訊息處理程式程式碼和/或呼叫預設值

    m_nTime+=100;//毫秒為單位

    CStringstr;

    str.Format("時間:%.1f秒",m_nTime/1000.0);

    GetDlgItem(IDC_STATIC2)->SetWindowText(str);

    CDialog::OnTimer(nIDEvent);

}

//訊息處理函式

LRESULTCMulitThread_2Dlg::OnGameOver(WPARAMwParam,LPARAMlParam)

{

    KillTimer(1);//關閉計時器

    if (wParam ==0)

    {//出錯

       GetDlgItem(IDC_STATIC1)->SetWindowText("出錯啦!");

       GetDlgItem(IDC_STATIC2)->SetWindowText("---");

       AfxMessageBox("出錯啦!",MB_OK|MB_ICONERROR);

    }

    else

    {//成功

       //顯示結果

       char *pName[] = {"張三","李四","王二","小蔡","趙幹"};

       CStringstr;

       str.Format("贏者:%s",pName[lParam]);

       GetDlgItem(IDC_STATIC1)->SetWindowText(str);

    }

    //使能開始按鈕,以便可以開始下一次比賽

    GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);

    return 0;

}

//開始比賽

voidCMulitThread_2Dlg::OnBnClickedButton1()

{

    // TODO:在此新增控制元件通知處理程式程式碼

    //使能開始按鈕:無效

    GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);

    m_nTime =0;//初始化時間為

    CDC *dc =GetDC();

    CRectrt;

    GetClientRect(rt);

    dc->FillSolidRect(40,0,rt.Width()-49,rt.Height()-50,RGB(240,240,240));//重新整理背景

    ReleaseDC(dc);

    //初始化執行緒的引數

    Info1[0].hWnd =Info1[1].hWnd =Info1[2].hWnd =Info1[3].hWnd =Info1[4].hWnd =GetSafeHwnd();

    Info1[0].nOffset = 0;Info1[1].nOffset = 90;Info1[2].nOffset = 180;Info1[3].nOffset = 270;Info1[4].nOffset = 360;

    //建立執行緒

    for (inti = 0;i<5;i++)

    {  

       m_hThead[i] = CreateThread(NULL,0,ThreadProc1,&Info1[i],CREATE_SUSPENDED,&m_dwThreadID[i]);   

    }

    SetTimer(1,100,NULL);//開始計時

    GetDlgItem(IDC_STATIC1)->SetWindowText("進行中...");

    //開始執行

    for (inti = 0;i<5;i++)

    {  

       ResumeThread(m_hThead[i]); 

    }

    //開始執行監測結果執行緒

    Info2.hWnd =m_hWnd;

    Info2.phHandle =m_hThead;

    hThead =CreateThread(NULL,0,ThreadProc2,&Info2,0,NULL);

}

//比賽執行緒

DWORDWINAPI ThreadProc1(LPVOIDlpParam)

{

    threadInfo1*info = (threadInfo1*)lpParam;

    CDC *dc =CWnd::FromHandle(info->hWnd)->GetDC();

    for (inti=40;i<570;i+=2)

    {

       for (intj=0;j<1000;j++)

       {//重複操作,以便人眼觀察

           dc->Rectangle(CRect(i,info->nOffset,i+1,info->nOffset+80));

       }

    }

    DeleteObject(dc);

    return 0;

}

//監視執行緒:誰先完成比賽就結束

DWORDWINAPI ThreadProc2(LPVOIDlpParam)

{

    threadInfo2*info = (threadInfo2*)lpParam;

    DWORDdwRet = 0;

    //等待個執行緒中的一個完成

    dwRet =WaitForMultipleObjects(5,info->phHandle,FALSE,INFINITE);

    if (dwRet ==WAIT_FAILED)

    {//出錯啦

       ::SendMessage(info->hWnd,WM_GAMEOVER,0,0);

       return 0;

    }

    //終止各個執行緒

    for (inti=0;i<5;i++)

    {

       TerminateThread(info->phHandle[i],0);

    }

    //傳送比賽結果訊息

    ::SendMessage(info->hWnd,WM_GAMEOVER,1,dwRet-WAIT_OBJECT_0);

    return 0;

}

執行結果:

工程原始碼下載地址:

歡迎大家修改和指正。

注意事項:

1.    該程式連主執行緒一共7個執行緒。其中一個執行緒專門用於檢測5個比賽執行緒的執行結果檢測,為什麼要專門開這個執行緒而不在主執行緒中進行呢?主要是WaitForMultipleObjects()函式是一個阻塞函式,如果在主執行緒中執行該函式,將使整個程式的介面不能操作(如:不能移動視窗等),因為一直阻塞在WaitForMultipleObjects函式處,而不能處理其它訊息,不信大家可以試試。

2.    執行緒同步的兩個比較重要的函式為WaitForSingleObject()和WaitForMultipleObjects(),具體使用請參考MSDN。這兩個函式都是阻塞函式,一直等待授信的物件發生才返回。

3.    採用訊息的方式通知主執行緒的執行結果,該方法比較簡單有效。一般的多執行緒程式都是採用主執行緒負責顯示,輔助執行緒來完成比較耗時的任務,等任務完成後再通知主執行緒執行結果。

轉載請說明出處,謝謝。

相關推薦

no