2012-12-16 197 views
7

我寫多線程程序。安全地停止線程

我想問一下TerminateThreadExitThread有什麼區別?

這是當WM_DESTROY收到我的代碼片段:

void CleanAll() 
{ 
    DWORD dwExit[MAX_THREAD]; 
    for(int i = 0; i < MAX_THREAD; i++) 
    { 
     GetExitCodeThread(hThread[i], &dwExit[i]); 
     // I used ExitThread(dwExit[i]); previously 
     TerminateThread(hThread[i], dwExit[i]); 
     CloseHandle(hThread[i]); 
    } 
} 

我用ExitThread()以前,但在任務管理器中我的程序stikk,所以我將其更改爲TerminateThread()和我的程序從任務管理器了。

任何事先的解釋是非常讚賞。

回答

1

當你的線程完成後,你的過程是否應該完成?對於你所描述的問題可能有很多解釋。如果你想終止整個過程,只需撥打ExitProcess即可。

TerminateThread的問題是,這很可能導致內存泄漏。它不關心線程狀態,也不關心分配的資源。它也可能導致死鎖,這取決於你如何進行同步。換句話說,它不會優雅地終止它。

終止線程的最好方法是不要明確終止它。請勿從線程的函數中調用TerminateThreadExitThread,而只需撥打return。您可能需要使用原子標誌,或者觸發一個事件(或另一個同步方法)來指示線程何時應該終止。然後,您的線程應定期檢查該標誌(或事件)並在返回(終止)之前取消分配所有資源。

+0

感謝您的回覆jweyrich,我的問題解決了,我遵循Selbie的指示,正如您所說,它只是從線程函數返回。 – user1888256

10

TerminateThread部隊另一個線程退出。你應該避免不惜一切代價來調用它,因爲它會阻止線程死掉,而沒有任何清理的機會。這包括分配的任何CRT存儲器。

ExitThread是爲當前正在運行的線程停止自己很好,乾淨。當你在上面調用它時,可能會迫使主(UI)線程退出,並可能使正在運行的線程仍然徘徊。因此,您的程序仍在運行,如任務管理器中所證明的那樣。 GetExitCodeThread也可能失敗,因爲線程並沒有真正退出。

但是停止線程的正確方法是通過任何干淨的方式發出乾淨的信號,以便它能夠退出。 然後允許線程在允許主線程退出前自己退出。在下面的例子中,我使用了一個全局標誌來指示它們應該退出的線程。但是,假設你的線程總是有機會輪詢全局布爾狀態。另一個更清晰的方法是讓每個線程在事件句柄上調用WaitForSingleObject。當事件句柄被髮信號時,線程檢查全局變量並在需要時退出。

bool global_Need_ToExit; // use a bool or replace with an event handle the thread shoudl wait on 

void CleanAll() 
{ 
    //signal all threads to exit 
    global_Need_ToExit = true; 

    DWORD dwExit[MAX_THREAD]; 
    for(int i = 0; i < MAX_THREAD; i++) 
    { 
     // actually wait for the thread to exit 
     WaitForSingleObject(hThread[i], WAIT_INFINITE); 

     // get the thread's exit code (I'm not sure why you need it) 
     GetExitCodeThread(hThread[i], &dwExit[i]); 

     // cleanup the thread 
     CloseHandle(hThread[i]); 
     hThread[i] = NULL; 
    } 
} 

DWORD __stdcall YourThreadFunction(void* pData) 
{ 

    while (global_Need_To_Exit == false) 
    { 
     // do more work 
    } 

    return 0; // same as ExitThread(0); 
} 
+0

有了這個目的,我們不應該使用非原子類型而不要聲明它是volatile。編譯器通常會優化循環,因此其他線程可能看不到更新的值。通過聲明一個變量volatile,你可以告訴編譯器它應該每次從它的存儲位置重新讀取它的值,而不是假設它以前從處理器寄存器或緩存中讀取的值。除此之外,很好的答案! +1 – jweyrich

+0

@Selbie:謝謝Selbie,你的建議對我來說就像一個魅力 – user1888256

+0

@jweyrich - 同意原則。假設他在輪詢該變量之間至少做了一次非內聯函數調用,他應該可以,因爲編譯器不能假定變量在函數調用之間不會改變。他在技術上需要一個「條件變量」來提高跨多核/多處理器體系結構的安全性,這可以通過使用InterlockedExchange * funcs或該變量上的任何鎖定功能輕鬆實現。 – selbie

0

您可以使用SingleWaitObjects或函數QueueUserAPC但你必須確保該線程停止檢查這些異步對象或等待一個在它的結束,正常終止線程。在linux下可以使用的信號

+0

感謝您的回覆CMAF-CMAC。我的問題現在消失了。我只是按照Selbie的指示。 – user1888256

0

最終,您需要像迷你流程一樣查看每個線程(無論如何,這就是它們的底線)。爲了讓它乾淨地關閉,你需要額外的基礎設施,讓你 - 主線程 - 告訴它停止。

做到這一點的最簡單方法是一個信號系統,POSIX的發明者很早就發現了這個信號系統,因此直接暴露在操作系統中。這種系統最好的現實例子是美國郵政局。每個代理都有一個唯一的地址,他們可以從其他地址發送和接收郵件(信號),由代理決定什麼時候收到信號。有些是信息或垃圾,因此被忽視,而另一些則是高度優先的,你在危險時忽視它們。

在代碼級別,您需要以下內容才能使其運行良好(我爲圖像處理程序構建了其中一些代碼):
1)某些抽象的「事件」或「控制信號」對象。如果它們足夠,可以隨意使用默認的Windows事件或Linux信號。
2)某種類型的「管道」:操作系統通常會有一個字面的內核原語CALLED一個管道,它可以讓數據在另一端放入數據時關閉數據。這個人通常在一個進程內的通信量過大,但是你需要的對象在概念上是等價的。
3)線程內的代碼從管道中檢索信號並對其執行操作。 4)所有線程都能識別「停止」或「取消」信號。

當然,您可以使用TerminateThread強制中止線程,就像您可以使用taskkill或CTRL-C來終止進程一樣。 ExitThread是一種特定於Windows的方式來構建像我在(4)中描述的乾淨關閉信號,但您仍然需要使用while循環&「WaitForSingleObject」(3)以及Windows特定的句柄(2)來使其工作。

2

對不起,但最高票的答案說使用ExitThread,因爲它會「停止自己很好,乾淨」。 這根本不是真的。該文檔指出: ExitThread是退出C代碼中的線程的首選方法。但是,在C++代碼中,在調用任何析構函數或執行任何其他自動清理之前,該線程將退出。因此,在C++代碼中,你應該從你的線程函數返回。 我從一個論壇的建議使用ExitThread,然後花幾個小時想知道我所有的內存泄漏是從哪裏來的,我學到了這個難題。是的,這是C++,但這就是這個問題。實際上,即使對於C,也有一個警告「鏈接到靜態C運行時庫(CRT)的可執行文件中的線程應該使用_beginthread和_endthread進行線程管理,而不是CreateThread和ExitThread。當線程調用ExitThread時會導致內存泄漏。「 不要使用ExitThread而不知道後果!