作中我們經常會遇到要建立多個線程,以此方便於我們可以同時執行多個事件。這也是我們在VC開發中的一項基礎,雖然我曾多次用到,但也會常常忘記。這次記下來,作為一個筆記,為大家學習提供方便。
在VC中,無非是建立線程和寫線程函數
一、常規方法:純使用Platform SDKAPI.
1, 建立線程:
#include <windows.h>
在MFC中通常在OnInitDialog()下面建立線程
//定義參數:SerialControl
//------------------變數函數初始化調用地區--------
CSerialControl * m_SerialControl=new CSerialControl();
m_SerialControl->Create(NULL,"aa",WS_CHILD,CRect(0,0,0,0),this,2,NULL);
m_SerialControl->InitAllSerialPort();
//------------------------------------------------
HANDLE hThread1=CreateThread(NULL,0,DetectCar,(LPVOID)SerialControl,0,NULL);
CloseHandle(hThread1);//此處關閉線程的控制代碼,但不意味關閉線程,線程在程式退出時關閉
參數說明:
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//必須為NULL
DWORD dwStackSize, //一般為0 ,表示堆棧與外部大小相同
LPTHREAD_START_ROUTINE lpStartAddress, //線程函數名稱
LPVOID lpParameter, //傳遞給線程函數的參數,如果為多個,自訂結構體
DWORD dwCreationFlags, //0表示建立線程後立即啟動線程,如果不是立即啟動需要調用ResumeThread函數
LPDWORD lpThreadId);//用來標記該線程的名稱
2, 定義線程函數:
//函數的定義
static DWORD WINAPI DetectCar(LPVOID lpParameter); //一般用靜態函數
//remark:由於線程函數是靜態函數,如果要在函數中用到對象,必須通過
//函數的實現
/***************************************************
*作者:萬田
*時間:2007-13-03
*函數:DetectCar() 說明:檢測線程
****************************************************/
DWORD WINAPI CISSDlg::DetectCar(LPVOID lpParameter)
{
TRACE("Thread DetectCar is running/r/n");
CSerialControl* SControl=(CSerialControl*)lpParameter;
//define:record which road is car
int Carexit=0;
while (TRUE)//do this forever
{
//get:which road exit car
Carexit=SControl->m_GroudDetector1.CarExists();
***********
}
方法二:使用MFC全域函數
CWinThread* AfxBeginThread(( AFX_THREADPROC pfnThreadProc, LPVOID pParam, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
這個需要自訂 pfnThreadProc():UINT MyControllingFunction( LPVOID pParam );
及傳遞參數類型。。。。如果建立的是掛起線程,使用CWinThread->ResumeThread 執行
方法三:使用MFC全域函數
CWinThread* AfxBeginThread( CRuntimeClass* pThreadClass, int nPriority = THREAD_PRIORITY_NORMAL, UINT nStackSize = 0, DWORD dwCreateFlags = 0, LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL );
這裡需要從CWinThread 類中派生自己的線程類CMyWinThread ,
CRuntimeClass *rc=RUNTIME_CLASS(CMyWinThread)....利用返回CWinThread 對象的指標就可以對線程進行各種操作。
方法四:使用自己的線程類,並在堆中建立線程對象
class CMyWinThread : public CWinThread{...},要在衍生類別中實現自己的虛函數 run()....
CMyWinThread *mth = new CMyWinThread()
mth->Create(....) mth->ResumeThread()
DWORD WINAPI TBegin(LPVOID lpParameter)//線程調用
{
return 0;
}
-----------
DWORD dwThreadID;//
HANDLE h_Handle;//線程
if((h_Handle =CreateThread(NULL,0,TBegin,this,0,&dwThreadID))==NULL)
{
MessageBox(_T("不能建立線程"));
return;
}
CloseHandle(h_Handle);
--------------
6.5 終止線程的運行
若要終止線程的運行,可以使用下面的方法:
? 線程函數返回(最好使用這種方法)。
? 通過調用E x i t T h r e a d函數,線程將自行撤消(最好不要使用這種方法)。
? 同一個進程或另一個進程中的線程調用Te r m i n a t e T h r e a d函數(應該避免使用這種方法)。
? 包含線程的進程終止運行(應該避免使用這種方法)。
下面將介紹終止線程啟動並執行方法,並且說明線程終止運行時會出現什麼情況。
6.5.1 線程函數返回
始終都應該將線程設計成這樣的形式,即當想要線程終止運行時,它們就能夠返回。這是
確保所有線程資源被正確地清除的唯一辦法。
如果線程能夠返回,就可以確保下列事項的實現:
? 線上程函數中建立的所有C + +對象均將通過它們的撤消函數正確地撤消。
? 作業系統將正確地釋放線程堆棧使用的記憶體。
? 系統將線程的結束代碼(線上程的核心對象中維護)設定為線程函數的傳回值。
? 系統將遞減線程核心對象的使用計數。
6.5.2 ExitThread函數
可以讓線程調用E x i t T h r e a d函數,以便強制線程終止運行:
該函數將終止線程的運行,並導致作業系統清除該線程使用的所有作業系統資源。但是,C + +資源(如C + +類對象)將不被撤消。由於這個原因,最好從線程函數返回,而不是通過調用E x i t T h r e a d來返回(詳細說明參見第4章)。
當然,可以使用E x i t T h r e a d的d w E x i t T h r e a d參數告訴系統將線程的結束代碼設定為什麼。E x i t T h r e a d函數並不返回任何值,因為線程已經終止運行,不能執行更多的代碼。
注意終止線程啟動並執行最佳方法是讓它的線程函數返回。但是,如果使用本節介紹的方法,應該知道E x i t T h r e a d函數是Wi n d o w s用來撤消線程的函數。如果編寫C / C + +代碼,那麼決不應該調用E x i t T h r e a d。應該使用Visual C++運行期庫函數_ e n d t h r e a d e x。如果不使用M i c r o s o f t的Visual C++編譯器,你的編譯器供應商有它自己的E x i t T h r e a d的替代函數。不管這個替代函數是什麼,都必須使用。本章後面將說明_ e n d t h r e a d e x的作用和它的重要性。
6.5.3 Te r m i n a t e T h r e a d函數
調用Te r m i n a t e T h r e a d函數也能夠終止線程的運行:
第6章線程的基礎知識計計127
與E x i t T h r e a d不同,E x i t T h r e a d總是撤消調用的線程,而Te r m i n a t e T h r e a d能夠撤消任何線程。h T h r e a d參數用於標識被終止啟動並執行線程的控制代碼。當線程終止運行時,它的結束代碼成為你作為d w E x i t C o d e參數傳遞的值。同時,線程的核心對象的使用計數也被遞減。
注意Te r m i n a t e T h r e a d函數是非同步啟動並執行函數,也就是說,它告訴系統你想要線程終止運行,但是,當函數返回時,不能保證線程被撤消。如果需要確切地知道該線程已經終止運行,必須調用Wa i t F o r S i n g l e O b j e c t (第9章介紹)或者類似的函數,傳遞線程的
控制代碼。
設計良好的應用程式從來不使用這個函數,因為被終止啟動並執行線程收不到它被撤消的通知。線程不能正確地清除,並且不能防止自己被撤消。注意當使用返回或調用E x i t T h r e a d的方法撤消線程時,該線程的記憶體堆棧也被撤消。但是,如果使用Te r m i n a t e T h r e a d,那麼在擁有線程的進程終止運行之前,系統不撤消該線程的堆棧。M i c r o s o f t故意用這種方法來實現Te r m i n a t e T h r e a d。如果其他仍然正在執行的線程要引用強制撤消的線程堆棧上的值,那麼其他的線程就會出現訪問違規的問題。如果將已經撤消的線程的堆棧留在記憶體中,那麼其他線程就可以繼續很好地運行。此外,當線程終止運行時, D L L通常接收通知。如果使用Terminate Thread 強迫線程終止,D L L就不接收通知,這能阻止適當的清除(詳細資料參見第2 0章)。
6.5.4 在進程終止運行時撤消線程
第4章介紹的E x i t P r o c e s s和Te r m i n a t e P r o c e s s函數也可以用來終止線程的運行。差別在於這些線程將會使終止啟動並執行進程中的所有線程全部終止運行。另外,由於整個進程已經被關閉,進程使用的所有資源肯定已被清除。這當然包括所有線程的堆棧。這兩個函數會導致進程中的剩餘線程被強制撤消,就像從每個剩餘的線程調用Te r m i n a t e T h r e a d一樣。顯然,這意味著正確的應用程式清除沒有發生,即C + +對象撤消函數沒有被調用,資料沒有轉至磁碟等等。