2012-12-16 148 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而不知道后果!