安全地停止线程

时间:2012-12-16 03:33:25

标签: c++ multithreading winapi

我写多线程程序。

我想问一下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(),但是我的程序在任务管理器中激活,所以我将其更改为TerminateThread(),我的程序从任务管理器中消失。

非常感谢任何预先解释。

5 个答案:

答案 0 :(得分:10)

TerminateThread强制另一个帖子退出。你应该不惜一切代价避免调用它,因为它会阻止一个线程死在它的轨道上而没有任何机会清理。这包括分配的任何CRT内存。

ExitThread用于当前正在运行的线程,以便让自己保持干净利落。当你在上面调用它时,你可能会强制主(UI)线程退出,并且可能使正在运行的线程仍然挥之不去。因此,您的程序仍在运行,如任务管理器中所示。由于线程实际上没有退出,因此GetExitCodeThread也可能失败。

但是,停止线程的正确方法是通过任何必要的干净方式干净地发出信号,它应该退出。 然后允许线程自行退出,然后才允许主线程退出。在下面的示例中,我使用了一个全局标志来向线程指示它们应该退出。但这假设你的线程总是有机会轮询全局bool状态。另一个更简洁的方法是让每个线程在事件句柄上调用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);
}

答案 1 :(得分:3)

很抱歉,但最高投票的回答是说要使用ExitThread,因为它会“让自己变得干净利落”。 这是不正确的。文件说明: ExitThread是退出C代码中线程的首选方法。但是,在C ++代码中,在调用任何析构函数或执行任何其他自动清理之前退出该线程。因此,在C ++代码中,您应该从线程函数返回。 我从一个论坛的建议中学习了这一点,以便使用ExitThread,然后花费数小时思考我所有的内存泄漏来自哪里。是的,这是针对C ++的,但这就是这个问题的意义所在。实际上,即使对于C,也有一个警告 “链接到静态C运行时库(CRT)的可执行文件中的线程应该使用_beginthread和_endthread进行线程管理而不是CreateThread和ExitThread。如果线程调用ExitThread,则无法执行此操作会导致内存泄漏。“ 不要在不知道后果的情况下使用ExitThread!

答案 2 :(得分:1)

当您的线程完成后,您的进程是否应该完成?对于您描述的问题,可能会有很多解释。如果您想终止整个过程,只需致电ExitProcess

TerminateThread的问题是很可能导致内存泄漏。它不关心线程状态,也不关心它分配的资源。它也可能导致死锁,具体取决于您如何进行同步。换句话说,它不会优雅地终止它。

终止线程的最佳方法是不显式终止它。不要从线程的函数中调用TerminateThreadExitThread,而只调用return。您可能需要使用原子标志,或者触发事件(或其他同步方法)以在线程终止时发出信号。然后你的线程应该定期检查那个标志(或事件)并在返回(终止)之前释放所有资源。

答案 3 :(得分:0)

您可以使用SingleWaitObjects或QueueUserAPC,但您必须确保线程停止检查这些异步对象或等待其中的一个并正常终止该线程..在linux中您可以使用信号

答案 4 :(得分:0)

最终,你需要像迷你进程一样查看每个线程(无论如何,这都是它们的内幕)。要让它干净地关闭,你需要额外的基础设施,让你 - 主线程 - 告诉它停止。

正确执行此操作的最简单方法是信号系统,POSIX的发明者在早期就已经找到并因此直接暴露在操作系统中。这种系统最好的现实世界的例子是美国邮局。每个代理都有一个唯一的地址,他们可以从这个地址发送和接收来自其他地址的邮件(信号),并由代理人决定他们何时收到信号。有些是信息或垃圾,因而被忽略,而其他人则是高优先级,你可以忽视它们,这是危险的。

在代码级别,您需要以下内容才能使其正常工作(我为图像处理程序构建了一些这样的程序):
1)一些抽象的“事件”或“ControlSignal”对象。如果它们足够,请随意使用默认的Windows事件或linux信号 2)某种类型的“管道”:操作系统通常会有一个文字内核原语CALLED管道,它允许1端关闭数据而另一端放入数据。这个人通常对于进程中的通信有点过分,但对象是你需要在概念上是等同的 3)线程中的代码,用于从管道中检索信号并对其进行操作。 4)所有线程识别出“停止”或“取消”信号。

当然,您可以使用TerminateThread强制中止线程,就像使用taskkill或CTRL-C来终止进程一样。 ExitThread是一种特定于Windows的方法来构建一个干净的关闭信号,就像我在(4)中描述的那样,但你仍然需要while-loop&amp; “WaitForSingleObject”(3)加上特定于Windows的句柄(2)使其正常工作。