C/C++線程退出的四種方法小結(jié)
想要終止線程的運(yùn)行,可以使用以下方法:
- 線程函數(shù)返回(最好使用該方法)。
- 同一個(gè)進(jìn)程或另一個(gè)進(jìn)程中的線程調(diào)用TerminateThread函數(shù)(應(yīng)避免使用該方法)。
- 通過調(diào)用ExitThread函數(shù),線程將自行撤消(最好不使用該方法)。
- ExitProcess和TerminateProcess函數(shù)也可以用來終止線程的運(yùn)行(應(yīng)避免使用該方法)。
下面將詳細(xì)介紹終止線程運(yùn)行的方法:1-4,并說明線程終止運(yùn)行時(shí)會(huì)出現(xiàn)何種情況:5。
為了說明以下線程的退出時(shí)發(fā)生的動(dòng)作,引入以下測(cè)試代碼:
class ?TTThread
{
public:
? ? /** @name Constructors and Destructor*/
? ? TTThread();
? ? virtual ~TTThread();
public:
?? ?//線程創(chuàng)建函數(shù)
?? ?BOOL Create();
?? ?//線程銷毀,在這里驗(yàn)證TerminateThread
?? ?void Destory();
?? ?//線程等待
?? ?BOOL Wait(DWORD dwWaitTime);
?? ?inline DWORD GetThreadID() { return m_dwThreadID; }
protected:
?? ?//線程函數(shù)邏輯接口,子類繼承
?? ?//在這里驗(yàn)證ExitThread和正常退出操作
?? ?virtual unsigned Process();
private:
?? ?//線程入口地址
?? ?static unsigned ?__stdcall _threadProc(void *lpParam);
private:
?? ?HANDLE?? ??? ?m_hThread;
?? ?DWORD?? ??? ?m_dwThreadID;
};
//C++對(duì)象
class CObj
{
public:
? ? CObj()
? ? {
? ? ? printf("CObj create...\n");
? ? }
? ? ~CObj()
? ? {
? ? ? ? printf("CObj delete...\n");
? ? }
};
//線程測(cè)試類
class CTestThread : public TTThread
{
public:
? ? virtual unsigned Process()
? ? {
? ? ? ?return 0;
? ? }
};線程函數(shù)返回
始終都應(yīng)該將線程設(shè)計(jì)成這樣的形式,即當(dāng)想要線程終止運(yùn)行時(shí),它們就能夠返回。這是確保所有線程資源被正確地清除的唯一辦法。
如果線程能夠返回,就可以確保下列事項(xiàng)的實(shí)現(xiàn):
(1)在線程函數(shù)中創(chuàng)建的所有C++對(duì)象均將通過它們的析構(gòu)函數(shù)進(jìn)行釋放。
(2)操作系統(tǒng)將正確地釋放線程堆棧使用的內(nèi)存。
(3)系統(tǒng)將線程的退出代碼(在線程的內(nèi)核對(duì)象中維護(hù))設(shè)置為線程函數(shù)的返回值。
(4)系統(tǒng)將遞減線程內(nèi)核對(duì)象的使用計(jì)數(shù)。
其中,(1)(2)兩點(diǎn)在編碼中需要特別關(guān)注的,這個(gè)在編碼規(guī)范上比較重要。(jimmy注)
測(cè)試代碼:
int _tmain(int argc, _TCHAR* argv[])
{
? ? printf("main thread begin...\n");
? ? //創(chuàng)建線程
? ? CTestThread ?workthread;
? ? workthread.Create();
? ? //等待線程結(jié)束,此時(shí)不會(huì)執(zhí)行Destroy函數(shù),線程正常結(jié)束
? ? if (!workthread.Wait(3 * 1000))
? ? {
? ? ? ? //超時(shí)銷毀線程 ? ? ??
? ? ? ? workthread.Destory();
? ? }
? ? printf("main thread end...\n");
? ? return 0;
}測(cè)試結(jié)果:
main thread begin...
CObj create...//c++對(duì)象創(chuàng)建
current index:0
current index:1
current index:2
current index:3
current index:4
CObj delete...//C++對(duì)象析構(gòu)以及線程申請(qǐng)的內(nèi)存正確釋放
main thread end...
請(qǐng)按任意鍵繼續(xù). . .
TerminateThread函數(shù)
調(diào)用TerminateThread函數(shù)也能夠終止線程的運(yùn)行,其函數(shù)原型如下:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
TerminateThread能夠撤消任何線程,其中hThread參數(shù)用于標(biāo)識(shí)被終止運(yùn)行的線程的句柄。當(dāng)線程終止運(yùn)行時(shí),它的退出代碼成為你作為dwExitCode參數(shù)傳遞的值。同時(shí),線程的內(nèi)核對(duì)象的使用計(jì)數(shù)也被遞減。
注意TerminateThread函數(shù)是異步運(yùn)行的函數(shù),也就是說,它告訴系統(tǒng)你想要線程終止運(yùn)行,但是,當(dāng)函數(shù)返回時(shí),不能保證線程被撤消。如果需要確切地知道該線程已經(jīng)終止運(yùn)行,必須調(diào)用WaitForSingleObject或者類似的函數(shù),傳遞線程的句柄。
用TerminateThread銷毀線程示例代碼:
// 在另一個(gè)線程中進(jìn)行調(diào)用,這里是在主線程終止測(cè)試線程
void TTThread::Destory()
{
if (m_hThread)
{
//TerminateThread以異步方式執(zhí)行,函數(shù)返回不代表線程結(jié)束,
//線程函數(shù)停止執(zhí)行,位置隨機(jī),類對(duì)象不會(huì)被析構(gòu)導(dǎo)致內(nèi)存泄漏
::TerminateThread(m_hThread, 0);
//等待線程結(jié)束
::WaitForSingleObject(m_hThread, 500);
::CloseHandle(m_hThread);
m_hThread = 0;
m_dwThreadID = 0;
printf("end destroy\n");
}
}設(shè)計(jì)良好的應(yīng)用程序從來不使用這個(gè)函數(shù),因?yàn)楸唤K止運(yùn)行的線程收不到它被撤消的通知。并且,如果使用TerminateThread,那么在擁有線程的進(jìn)程終止運(yùn)行之前,系統(tǒng)不撤消該線程的堆棧,造成內(nèi)存不能及時(shí)釋放。
int _tmain(int argc, _TCHAR* argv[])
{
? ? printf("main thread begin...\n");
? ? //創(chuàng)建線程
? ? CTestThread ?workthread;
? ? workthread.Create();
? ? //等待線程結(jié)束,此時(shí)會(huì)執(zhí)行Destroy函數(shù),線程異常結(jié)束
? ? if (!workthread.Wait(1 * 1000))
? ? {
? ? ? ? //超時(shí),主線程銷毀測(cè)試線程 ? ? ??
? ? ? ? workthread.Destory();
? ? }
? ? printf("main thread end...\n");
? ? return 0;
}測(cè)試結(jié)果
main thread begin...
CObj create...
current index:0
current index:1
end destroy
main thread end...
//CObj沒有執(zhí)行析構(gòu)函數(shù),沒有及時(shí)釋放線程申請(qǐng)的_tiddata內(nèi)存
請(qǐng)按任意鍵繼續(xù). . .
ExitThread函數(shù)
可以讓線程調(diào)用ExitThread函數(shù),以便強(qiáng)制終止當(dāng)前線程運(yùn)行,其函數(shù)原型:
VOID ExitThread(DWORD dwExitCode);
該函數(shù)將終止線程的運(yùn)行,并導(dǎo)致操作系統(tǒng)清除該線程使用的所有操作系統(tǒng)資源。但是,C++資源(如C++類對(duì)象)將不被析構(gòu)。由于這個(gè)原因,最好從線程函數(shù)返回,而不是通過調(diào)用ExitThread來返回。
當(dāng)然,可以使用ExitThread的dwExitThread參數(shù)告訴系統(tǒng)將線程的退出代碼設(shè)置為什么。ExitThread函數(shù)并不返回任何值,因?yàn)榫€程已經(jīng)終止運(yùn)行,不能執(zhí)行更多的代碼。
注意終止線程運(yùn)行的最佳方法是讓它的線程函數(shù)返回。但是,如果使用本節(jié)介紹的方法,應(yīng)該知道ExitThread函數(shù)是Windows用來撤消線程的函數(shù)。如果編寫C/C++代碼,那么決不應(yīng)該調(diào)用ExitThread。應(yīng)該使用Visual C++運(yùn)行期庫函數(shù)_endthreadex,因?yàn)開endthreadex可以確保,及時(shí)釋放線程申請(qǐng)的tiddata內(nèi)存。
測(cè)試代碼
class CTestThread : public TTThread
{
public:
? ? virtual unsigned Process()
? ? {
? ? ? ? CObj obj;
? ? ? ?for (int i = 0; i < 5; i++)
? ? ? ?{
? ? ? ? ? ?Sleep(400);
? ? ? ? ? ?printf("current index:%d\n", i);
? ? ? ? ? ?//當(dāng)前線程退出
? ? ? ? ? ?if (i == 3)
? ? ? ? ? ?{
? ? ? ? ? ? ? ?printf("make thread end...\n");
? ? ? ? ? ? ? ?//_endthreadex(0);
? ? ? ? ? ? ? ?ExitThread(0);
? ? ? ? ? ?}
? ? ? ?}
? ? ? ?return 0;
? ? }
};
int _tmain(int argc, _TCHAR* argv[])
{
? ? printf("main thread begin...\n");
? ? //創(chuàng)建線程
? ? CTestThread ?workthread;
? ? workthread.Create();
?? ?//等待線程結(jié)束
? ? if (workthread.Wait(3 * 1000))
? ? {
? ? ? ? printf("the test thread end...");
? ? }
? ? printf("main thread end...\n");
? ? return 0;
}測(cè)試結(jié)果
main thread begin...
CObj create...
current index:0
current index:1
current index:2
current index:3
make thread end...線程被中斷執(zhí)行,造成tiddata和CObj內(nèi)存泄漏
the test thread end...main thread end...
請(qǐng)按任意鍵繼續(xù). . .
在進(jìn)程終止運(yùn)行時(shí)撤消線程
ExitProcess和TerminateProcess函數(shù)也可以用來終止線程的運(yùn)行。差別在于這些線程將會(huì)使終止運(yùn)行的進(jìn)程中的所有線程全部終止運(yùn)行。另外,由于整個(gè)進(jìn)程已經(jīng)被關(guān)閉,進(jìn)程使用的所有資源肯定已被清除。這當(dāng)然包括所有線程的堆棧。這兩個(gè)函數(shù)會(huì)導(dǎo)致進(jìn)程中的剩余線程被強(qiáng)制撤消,就像從每個(gè)剩余的線程調(diào)用TerminateThread一樣。顯然,這意味著正確的應(yīng)用程序清除沒有發(fā)生,即C++對(duì)象撤消函數(shù)沒有被調(diào)用,數(shù)據(jù)沒有轉(zhuǎn)至磁盤等等。
線程終止運(yùn)行時(shí)發(fā)生的操作
當(dāng)線程終止運(yùn)行時(shí),會(huì)發(fā)生下列操作:
(1)線程擁有的所有用戶對(duì)象均被釋放。在Windows中,大多數(shù)對(duì)象是由包含創(chuàng)建這些對(duì)象的線程的進(jìn)程擁有的。但是一個(gè)線程擁有兩個(gè)用戶對(duì)象,即窗口和掛鉤。當(dāng)線程終止運(yùn)行時(shí),系統(tǒng)會(huì)自動(dòng)撤消任何窗口,并且卸載線程創(chuàng)建的或安裝的任何掛鉤。其他對(duì)象只有在擁有線程的進(jìn)程終止運(yùn)行時(shí)才被撤消。
(2)線程的退出代碼從STILL_ACTIVE改為傳遞給ExitThread或TerminateThread的代碼。
(3)線程內(nèi)核對(duì)象的狀態(tài)變?yōu)橐淹ㄖ?br />(4)如果線程是進(jìn)程中最后一個(gè)活動(dòng)線程,系統(tǒng)也將進(jìn)程視為已經(jīng)終止運(yùn)行。
(5)線程內(nèi)核對(duì)象的使用計(jì)數(shù)遞減1。
當(dāng)一個(gè)線程終止運(yùn)行時(shí),在與它相關(guān)聯(lián)的線程內(nèi)核對(duì)象的所有未結(jié)束的引用關(guān)閉之前,該內(nèi)核對(duì)象不會(huì)自動(dòng)被釋放。
一旦線程不再運(yùn)行,系統(tǒng)中就沒有別的線程能夠處理該線程的句柄。然而別的線程可以調(diào)用GetExitcodeThread來檢查由hThread標(biāo)識(shí)的線程是否已經(jīng)終止運(yùn)行。如果它已經(jīng)終止運(yùn)行,則確定它的退出代碼:
函數(shù)原型:
BOOL GetExitCodeThread( HANDLE hThread, PDWORD pdwExitCode);
退出代碼的值在pdwExitCode指向的DWORD中返回。如果調(diào)用GetExitCodeThread時(shí)線程尚未終止運(yùn)行,該函數(shù)就用STILL_ACTIVE標(biāo)識(shí)符(定義為0x103)填入DWORD。如果該函數(shù)運(yùn)行成功,便返回TRUE。
線程基類整體實(shí)現(xiàn):
/*******************************************************?
?* ?@file ? ? ?TTThread.cpp
?* ?@author ? ?jimmy
?* ?@brief ? windows線程處理的包裝
?******************************************************/
#include "stdafx.h"
#include "TTThread.h"
TTThread::TTThread()
:m_hThread(0)
{
}
TTThread::~TTThread()
{
?? ?if (m_hThread)
? ? {
? ? ? ? ::CloseHandle(m_hThread);
? ? }
?? ?m_hThread = 0;
}
/**************************************************************
* ?@brief : TTThread::create
* ? ??
* ?@param : -none
* ? ??
* ?@return : BOOL
* ? ??
* ?@author : Jimmy
* ? ??
* ?@date : 2019/2/13 星期三
* ? ??
* ?@note : 線程創(chuàng)建函數(shù)
***************************************************************/
BOOL TTThread::Create()
{
? ? m_hThread = (HANDLE)_beginthreadex(0,0, _threadProc, this, 0, (unsigned*)&m_dwThreadID);
? ? if (0 == m_hThread)
? ? {
? ? ? ? m_dwThreadID = 0;
? ? }
?? ?return m_hThread >(HANDLE)1;
}
void TTThread::Destory()
{
?? ?if (m_hThread)
?? ?{
?? ??? ?//TerminateThread以異步方式執(zhí)行,函數(shù)返回不代表線程結(jié)束,
? ? ? ? //線程函數(shù)停止執(zhí)行,位置隨機(jī),類對(duì)象不會(huì)被析構(gòu)導(dǎo)致內(nèi)存泄漏
? ? ? ? ::TerminateThread(m_hThread, 0);
? ? ? ? //等待線程結(jié)束
? ? ? ? ::WaitForSingleObject(m_hThread, 500);
?? ??? ?::CloseHandle(m_hThread);
?? ??? ?m_hThread = 0;
?? ??? ?m_dwThreadID = 0;
? ? ? ? printf("end destroy\n");
?? ?}
}
BOOL TTThread::Wait(DWORD dwWaitTime)
{
?? ?if (m_hThread == 0)
?? ??? ?return TRUE;
?? ?return (::WaitForSingleObject(m_hThread, dwWaitTime) != WAIT_TIMEOUT);
}
unsigned TTThread::Process()
{
?? ?return 0;
}
unsigned __stdcall TTThread::_threadProc(void *lpParam)
{
?? ?TTThread* pThread = (TTThread*)lpParam;
?? ?assert(pThread);
?? ?if (pThread != 0)
?? ?{
?? ??? ?pThread->Process();
?? ?}
?? ?return 0;
}參考文章:
https://www.cnblogs.com/arsblog/p/4829729.html
完整線程類代碼參見github:
https://github.com/jinxiang1224/cpp/tree/master/thread
到此這篇關(guān)于C/C++線程退出的四種方法小結(jié)的文章就介紹到這了,更多相關(guān)C/C++線程退出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言實(shí)現(xiàn)數(shù)獨(dú)小游戲
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)數(shù)獨(dú)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
Matlab實(shí)現(xiàn)貪吃蛇小游戲的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用Matlab實(shí)現(xiàn)貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
C++中對(duì)象的賦值與復(fù)制操作詳細(xì)解析
對(duì)象之間的賦值也是通過賦值運(yùn)算符“=”進(jìn)行的。本來賦值運(yùn)算符“=”只能用來對(duì)單個(gè)的變量賦值,現(xiàn)在被擴(kuò)展為兩個(gè)同類對(duì)象之間的賦值,這是通過對(duì)賦值運(yùn)算符的重載實(shí)現(xiàn)的2013-10-10

