採用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?- //執行緒函式宣告
- DWORD WINAPI ThreadProc(LPVOIDlpParam);
- //為了傳遞多個引數,我採用結構體
- struct threadInfo
- {
- HWND hWnd; //視窗控制代碼
- int nOffset; //偏移量
- COLORREF clrRGB; //顏色
- };
- protected:
- HANDLE hThead[3]; //用於儲存執行緒控制代碼
- DWORD dwThreadID[3];//用於儲存執行緒的ID
- threadInfo Info[3]; //傳遞給執行緒處理函式的引數
//實現檔案中
[cpp] view plain copy print?- //單執行緒測試
- void CMultiThread_1Dlg::OnBnClickedButton1()
- {
- // TODO: 在此新增控制元件通知處理程式程式碼
- //使能按鈕
- GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
- GetDlgItem(IDC_BUTTON2)->EnableWindow(FALSE);
- CDC *dc = GetDC();
- CRect rt;
- 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");
- //#1
- for (int i=0;i<460;i++)
- {
- for (int j =10 ;j<200;j++)
- {
- dc->SetPixel(j,460-i,RGB(255,0,0));
- }
- }
- //#2
- for (int i=0;i<460;i++)
- {
- for (int j =210 ;j<400;j++)
- {
- dc->SetPixel(j,460-i,RGB(0,255,0));
- }
- }
- //#3
- for (int i=0;i<460;i++)
- {
- for (int j =410 ;j<600;j++)
- {
- dc->SetPixel(j,460-i,RGB(0,0,255));
- }
- }
- ReleaseDC(dc);
- //使能按鈕
- GetDlgItem(IDC_BUTTON1)->EnableWindow(TRUE);
- GetDlgItem(IDC_BUTTON2)->EnableWindow(TRUE);
- }
- //多執行緒測試
- void CMultiThread_1Dlg::OnBnClickedButton2()
- {
- // TODO: 在此新增控制元件通知處理程式程式碼
- CDC *dc = GetDC();
- CRect rt;
- 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].hWnd = Info[1].hWnd = Info[2].hWnd = GetSafeHwnd();
- 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 (int i = 0;i<3;i++)
- {
- hThead[i] = CreateThread(NULL,0,ThreadProc,&Info[i],0,&dwThreadID[i]);
- }
- ReleaseDC(dc);
- }
- DWORD WINAPI ThreadProc(LPVOIDlpParam)
- {
- threadInfo*Info = (threadInfo*)lpParam;
- CDC *dc = CWnd::FromHandle(Info->hWnd)->GetDC();
- for (int i=0;i<460;i++)
- {
- for (int j=Info->nOffset;j<Info->nOffset+190;j++)
- {
- dc->SetPixel(j,460-i,Info->clrRGB);
- }
- }
- DeleteObject(dc);
- return 0;
- }
執行效果:
單執行緒測試
多執行緒測試
工程原始碼下載地址:
歡迎大家修改和指正。
注意事項:
(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. 採用訊息的方式通知主執行緒的執行結果,該方法比較簡單有效。一般的多執行緒程式都是採用主執行緒負責顯示,輔助執行緒來完成比較耗時的任務,等任務完成後再通知主執行緒執行結果。
轉載請說明出處,謝謝。