采用CreateThread()创建多线程程序[通俗易懂]
采用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 WINAPIThreadProc (LPVOID lpParam);//格式不正確將無法調用成功。函數名稱沒有限制,只要符合命名規則就可以。
但我常??吹接邢铝械木€程函數定義:
void ThreadProc ();//該格式也是可以的,但使用的時候要這樣通過
LPTHREAD_START_ROUTINE轉換,如:
(LPTHREAD_START_ROUTINE)ThreadProc
我建議還是使用規范的格式比較好,不推薦使用void ThreadProc ()格式。不信就請看看MSDN的說明吧:
Do not declare this callback function with a void 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)為了演示效果,采用了比較耗時的打點處理。
主要的函數如下:
在頭文件的定義
//線程函數聲明DWORD WINAPI ThreadProc(LPVOIDlpParam);//為了傳遞多個參數,我采用結構體struct threadInfo{ HWND hWnd; //窗口句柄 int nOffset; //偏移量 COLORREF clrRGB; //顏色}; protected:HANDLE hThead[3]; //用于存儲線程句柄 DWORD dwThreadID[3];//用于存儲線程的ID threadInfo Info[3]; //傳遞給線程處理函數的參數
//實現文件中
//單線程測試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;}
運行效果:
單線程測試
多線程測試
工程源碼下載地址:
http://download.csdn.net/detail/cbnotes/4857152
歡迎大家修改和指正。
注意事項:
(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指針,以便直接調用。
struct threadInfo
{
CDC * dc; //畫布
int nOffset; //偏移量
COLORREF clrRGB; //顏色
};
//多線程測試
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].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 (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;
for (int i=0;i<460;i++)
{
for (int j=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 : 頭文件
//聲明線程處理函數
DWORD WINAPI ThreadProc1(LPVOIDlpParam);
DWORD WINAPI ThreadProc2(LPVOIDlpParam);
//為了傳遞多個參數,我采用結構體
struct threadInfo1
{
HWND hWnd; //窗口句柄
int nOffset; //偏移量
};
struct threadInfo2
{
HWND hWnd;//窗口句柄
HANDLE *phHandle;//偏移量
};
protected:
long m_nTime;//時間
HANDLE m_hThead[5];//用于存儲線程句柄
HANDLE hThead; //用于存儲線程句柄
DWORD m_dwThreadID[5];//用于存儲線程的ID
threadInfo1Info1[5];//傳遞給線程處理函數的參數
threadInfo2Info2;
// MulitThread_2Dlg.cpp : 實現文件
//更新時間:毫秒
void CMulitThread_2Dlg::OnTimer(UINT_PTRnIDEvent)
{
// TODO: 在此添加消息處理程序代碼和/或調用默認值
m_nTime+=100;//毫秒為單位
CString str;
str.Format(“時間:%.1f秒”,m_nTime/1000.0);
GetDlgItem(IDC_STATIC2)->SetWindowText(str);
CDialog::OnTimer(nIDEvent);
}
//消息處理函數
LRESULT CMulitThread_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;
}
//開始比賽
void CMulitThread_2Dlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知處理程序代碼
//使能開始按鈕:無效
GetDlgItem(IDC_BUTTON1)->EnableWindow(FALSE);
m_nTime =0;//初始化時間為
CDC *dc = GetDC();
CRect rt;
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 (int i = 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 (int i = 0;i<5;i++)
{
ResumeThread(m_hThead[i]);
}
//開始運行監測結果線程
Info2.hWnd = m_hWnd;
Info2.phHandle = m_hThead;
hThead = CreateThread(NULL,0,ThreadProc2,&Info2,0,NULL);
}
//比賽線程
DWORD WINAPI ThreadProc1(LPVOIDlpParam)
{
threadInfo1*info = (threadInfo1*)lpParam;
CDC *dc = CWnd::FromHandle(info->hWnd)->GetDC();
for (int i=40;i<570;i+=2)
{
for (int j=0;j<1000;j++)
{
//重復操作,以便人眼觀察
dc->Rectangle(CRect(i,info->nOffset,i+1,info->nOffset+80));
}
}
DeleteObject(dc);
return 0;
}
//監視線程:誰先完成比賽就結束
DWORD WINAPI ThreadProc2(LPVOIDlpParam)
{
threadInfo2*info = (threadInfo2*)lpParam;
DWORD dwRet = 0;
//等待個線程中的一個完成
dwRet = WaitForMultipleObjects(5,info->phHandle,FALSE,INFINITE);
if (dwRet == WAIT_FAILED)
{
//出錯啦
::SendMessage(info->hWnd,WM_GAMEOVER,0,0);
return 0;
}
//終止各個線程
for (int i=0;i<5;i++)
{
TerminateThread(info->phHandle[i],0);
}
//發送比賽結果消息
::SendMessage(info->hWnd,WM_GAMEOVER,1,dwRet– WAIT_OBJECT_0);
return 0;
}
運行結果:
工程源碼下載地址:
http://download.csdn.net/detail/cbnotes/4867333
歡迎大家修改和指正。
注意事項:
1.該程序連主線程一共7個線程。其中一個線程專門用于檢測5個比賽線程的運行結果檢測,為什么要專門開這個線程而不在主線程中進行呢?主要是WaitForMultipleObjects()函數是一個阻塞函數,如果在主線程中運行該函數,將使整個程序的界面不能操作(如:不能移動窗口等),因為一直阻塞在WaitForMultipleObjects函數處,而不能處理其它消息,不信大家可以試試。
2.線程同步的兩個比較重要的函數為WaitForSingleObject()和WaitForMultipleObjects(),具體使用請參考MSDN。這兩個函數都是阻塞函數,一直等待授信的對象發生才返回。
3.采用消息的方式通知主線程的運行結果,該方法比較簡單有效。一般的多線程程序都是采用主線程負責顯示,輔助線程來完成比較耗時的任務,等任務完成后再通知主線程運行結果。
轉載請說明出處,謝謝。
總結
以上是生活随笔為你收集整理的采用CreateThread()创建多线程程序[通俗易懂]的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 这款新工具可以解决7天签名问题哦! 来试
- 下一篇: 工厂模式和抽象工厂模式的区别?