MFC下結束AfxBeginThread開啟的線程的一些體會


本文轉自:http://hi.baidu.com/yjglg/item/c6f796e315f6a2266dabb835

最近,由於論文的需求,要用到Windows下的多線程。考慮到界面用MFC寫了,於是上網搜了下MFC下的多線程怎樣搞,都說用AfxBeginThread來日比較好。哥向來比較浮躁,先搜搜有沒相關代碼,找到幾個可用的,然后各種摘抄,於是乎將哥的播放器的幾個線程搞成下面這段代碼(摘要):

UINT playThread(LPVOID pParam){ //播放線程,固定格式
//......做變量聲明,賦值等前期工作
while(SomeCondition){ //播放線程的循環
//......播放音樂,不解析
}
return 0;
}

void CPlayerDlg::OnBnClickedPlay(){ //播放按鈕響應函數
if(isThreadPause){ //判斷是否暫停中
isThreadPause=false;
pPlayerThread->ResumeThread();//繼續播放
}
else{
OnBnClickedStop();
pPlayerThread=AfxBeginThread(playWaveThread,NULL); //開啟播放線程
}
}

void CPlayerDlg::OnBnClickedPause(){ //暫停響應函數
if(!isThreadPause){
PlayerThread->SuspendThread(); //掛起進程,相當於暫停播放
isThreadPause=true;
}
}

void CPlayerDlg::OnBnClickedStop(){ //終止響應函數
if(pPlayThread){
isThreadPause=false;
TerminateThread(pPlayerThread->m_hThread,0);//強行終止線程,這里有問題,后面說
}
}

  其中播放線程playThread的聲明是固定那種格式的,而且最好寫成全局函數,方便,如果寫成類成員函數的話又要加static,調用時又要加作用域的,十分蛋痛。寫完后果斷運行,yeah,能播放、暫停和停止,相當舒服,也沒去理會細節的問題。

  直到今天,心血來潮地打開任務管理器,看看程序內存占用情況,發現了一個狠嚴重的問題:每當我停止一首歌,播放下一首時,內存就突然間往上跳。一開始以為是正常的內存創建和回收造成的浮動,但我繼續不斷地重復播放停止、播放停止,發現內存一直往上升。雖然每次都只是上升一點點,但明擺着的memory leak擱在那,還不搞它哥以后怎樣出來混?

  好,果斷google之,發現問題出在TerminateThread這個函數。這個TerminateThread結束線程用的是相當暴力的方法,據說連里面的局部變量都不釋放。這就草了,馬上尋找解決辦法,有人回帖說用CreateEvent和WaitForSingleObject結合日之,解釋沒解釋清楚,給出的sample code也是相當糾結和羞澀,而且樓下跟帖說這種方法有可能阻塞死鎖之類的。果斷放棄,看到另外一種方法,就是在停止的響應函數里用::PostThreadMessage(由於播放線程是全局函數,所以前面要加::)給播放線程發送停止消息,播放線程里加一個MSG的變量和while,每次里面調用PeekMessage來檢查是否發來停止的消息,寫了下,代碼相當簡練明了:

#define WM_THREAD_STOP 0x0427 //自定義一個消息,也可以用系統定義的如WM_QUIT

UINT playWaveThread(LPVOID pParam){
//......做變量聲明,賦值等前期工作
while(SomeCondition){ //播放線程的循環
MSG msg; //增加一個MSG的變量msg來接收消息
while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){ //將消息隊列里的消息逐個讀入msg
if(msg.message==WM_THREAD_STOP){ //如果收到終止消息則退出
//TODO:放在堆里的變量要在這里手動清理
return 0; //線程正常返回,會釋放局部變量等內存資源
}
else{
DispatchMessage(&msg);//字面意思,不解釋
}
}
//......播放音樂,不解析
}
return 0;//正常播放結束,釋放資源
}

void CPlayerDlg::OnBnClickedPlay(){……}//播放按鈕響應函數,不變

void CPlayerDlg::OnBnClickedPause(){……}//暫停響應函數,也不變

void CPlayerDlg::OnBnClickedStop(){
if(pPlayerThread){
isThreadPause=false;
//原來的TerminateThread不用,換成下面這個
::PostThreadMessage(pPlayerThread->m_nThreadID,WM_THREAD_STOP,0,0);
}
}

  寫完,果斷運行並打開任務管理器監測,誒!果然沒有出現之前的內存一直在漲的現象,十分舒服,搞定收工!話說本人剛學多線程,代碼寫得相當的水,如果哪位大牛看到這處理方法還存在什么問題望不吝賜教,謝謝!

附錄:AfxBeginThread-線程介紹

CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc,
LPVOID pParam,
int nPriority = THREAD_PRIORITY_NORMAL,
UNT nStackSize = 0,
DWORD dwCreateFlags = 0,
LPSECURITY_ATTRIBUTES lpSecurityAttrs = NULL
);//用於創建工作者線程

返回值: 一個指向新線程的線程對象

pfnThreadProc : 線程的入口函數,聲明一定要如下: UINT MyThreadFunction( LPVOID pParam );
pParam : 傳遞入線程的參數,注意它的類型為:LPVOID,所以我們可以傳遞一個結構體入線程.
nPriority : 線程的優先級,一般設置為 0 .讓它和主線程具有共同的優先級.
nStackSize : 指定新創建的線程的棧的大小.如果為 0,新創建的線程具有和主線程一樣的大小的棧
dwCreateFlags : 指定創建線程以后,線程有怎么樣的標志.可以指定兩個值:
CREATE_SUSPENDED : 線程創建以后,會處於掛起狀態,真到調用: ResumeThread
0 : 創建線程后就開始運行.
lpSecurityAttrs : 指向一個 SECURITY_ATTRIBUTES 的結構體,用它來標志新創建線程的安全性.如果為 NULL ,

那么新創建的線程就具有和主線程一樣的安全性.
如果要在線程內結束線程,可以在線程內調用 AfxEndThread.

AfxBeginThread-結束線程的兩種方式

當你在后台用線程來打印一些圖形時.有時在打印一部分后,你希望可以停下來,那么此如何讓線程停止呢.下

面會詳細的向你解釋要結束線程的兩種方式

1 : 這是最簡單的方式,也就是讓線程函數執行完成,此時線程正常結束.它會返回一個值,一般0是成功結束,

當然你可以定義自己的認為合適的值來代表線程成功執行.在線程內調用AfxEndThread將會直接結束線程,此時線

程的一切資源都會被回收.
2 : 如果你想讓別一個線程B來結束線程A,那么,你就需要在這兩個線程中傳遞信息.
不管是工作者線程還是界面線程,如果你想在線程結束后得到它的確結果,那么你可以調用:

::GetExitCodeThread函數

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM