不使用TerminateThread()正常终止线程

时间:2009-11-09 16:35:04

标签: c windows multithreading

我的应用程序创建了一个线程,它始终在后台运行。我只能手动终止线程,而不是在线程回调函数内。 目前我正在使用TerminateThread()来杀死该线程但它有时会导致它挂起。 我知道有一种方法可以使用事件和WaitForSingleObject()来使线程优雅地终止,但是我找不到一个关于它的例子。

请在此处输入代码。

4 个答案:

答案 0 :(得分:19)

TerminateThread是一个坏主意,特别是如果您的线程使用互斥对象等同步对象。它可能导致未释放的内存和句柄,以及死锁,所以你是正确的,你需要做其他事情。

通常,线程终止的方式是从定义线程的函数返回。主线程通过使用事件对象或甚至是简单的布尔值来通知工作线程退出,如果它经常被检查的话。如果工作线程等待WaitForSingleObject,您可能需要将其更改为WaitForMultipleObjects,其中一个对象是事件。主线程将调用SetEvent,工作线程将唤醒并返回。

除非您向我们展示您正在做的事情,否则我们无法提供任何有用的代码。根据工作线程的作用以及主线程如何向其传递信息,它可能看起来非常不同。

此外,在[现在很旧] MSVC下,您需要使用_beginthreadex而不是CreateThread,以避免CRT中的内存泄漏。请参阅MSKB #104641

更新

工作线程的一种用法是作为“计时器”,定期进行一些操作。最琐碎的事情:

for (;;) {
    switch (WaitForSingleObject(kill_event, timeout)) {
        case WAIT_TIMEOUT: /*do timer action*/ break;
        default: return 0; /* exit the thread */
    }
}

另一种用途是按需做某事。基本相同,但超时设置为INFINITE并对WAIT_OBJECT_0而不是WAIT_TIMEOUT执行某些操作。在这种情况下,您需要两个事件,一个用于唤醒线程并执行某些操作,另一个用于唤醒并退出:

HANDLE handles[2] = { action_handle, quit_handle };
for (;;) {
    switch (WaitForMultipleObject(handles, 2, FALSE, INFINITE)) {
        case WAIT_OBJECT_0 + 0: /* do action */ break;
        default:
        case WAIT_OBJECT_0 + 1: /* quit */ break;
    }
}

请注意,如果WFSO / WFMO返回错误而不是预期结果之一,则循环执行合理的操作非常重要。在上面的两个例子中,我们只是将错误视为已经发出信号以便退出。

您可以通过从主线程关闭事件句柄来获得与第一个示例相同的结果,导致工作线程从WaitForSingleObject获取错误并退出,但我不建议采用这种方法。

答案 1 :(得分:1)

由于您不知道线程正在做什么,因此无法从外部安全地终止线程。

为什么你认为你不能从内部终止它?

您可以在启动线程之前创建一个事件,并将该事件的句柄传递给线程。您从主线程调用该事件的SetEvent()以指示线程停止,然后在线程句柄上调用WaitForSingleObject以等待线程实际完成。在线程循环中,您在事件上调用WaitForSingleObject(),指定超时0(零),以便即使未设置事件,调用也会立即返回。如果该调用返回WAIT_TIMEOUT,则不设置该事件,如果它返回WAIT_OBJECT_0,则设置该事件。在后一种情况下,您将从线程函数返回。

我认为你的线程不只是在无限循环中燃烧CPU周期,而是通过调用Sleep()来做一些等待。如果是这样,您可以通过向其传递超时来WaitForSingleObject进行休眠。

答案 2 :(得分:0)

你在后台线程做什么?如果你正在循环某些东西,你可以通过从前台线程设置为Boolean并且后台线程检查的共享公共静态对象(如true)来结束自身内部的线程设置为true时,可以干净地退出。

答案 3 :(得分:0)

这是以fork-join方式进行线程管理的代码示例。它使用struct Thread作为线程描述符。

让我们介绍一些线程描述符数据结构的抽象:

    #include <Windows.h>

    struct Thread
    {
        volatile BOOL stop;

        HANDLE event;
        HANDLE thread;
    };

    typedef DWORD ( __stdcall *START_ROUTINE)(struct Thread* self, LPVOID lpThreadParameter);

    struct BootstrapArg
    {
        LPVOID arg;
        START_ROUTINE body;
        struct Thread* self;
    };

父线程使用的函数:

  • StartThread()初始化此结构并启动新线程。
  • StopThread()启动线程终止并等待线程实际终止。

    DWORD __stdcall ThreadBootstrap(LPVOID lpThreadParameter)
    {
        struct BootstrapArg ba = *(struct BootstrapArg*)lpThreadParameter;
    
        free(lpThreadParameter);
    
        return ba.body(ba.self, ba.arg);
    }
    
    VOID StartThread(struct Thread* CONST thread, START_ROUTINE body, LPVOID arg)
    {
        thread->event = CreateEvent(NULL, TRUE, FALSE, NULL);
        thread->stop = FALSE;
        thread->thread = NULL;
    
        if ((thread->event != NULL) && (thread->event != INVALID_HANDLE_VALUE))
        {
            struct BootstrapArg* ba = (struct BootstrapArg*)malloc(sizeof(struct BootstrapArg));
    
            ba->arg = arg;
            ba->body = body;
            ba->self = thread;
    
            thread->thread = CreateThread(NULL, 0, ThreadBootstrap, ba, 0, NULL);
            if ((thread->thread == NULL) || (thread->thread == INVALID_HANDLE_VALUE))
            {
                free(ba);
            }
        }
    }
    
    DWORD StopThread(struct Thread* CONST thread)
    {
        DWORD status = ERROR_INVALID_PARAMETER;
    
        thread->stop = TRUE;
    
        SetEvent(thread->event);
        WaitForSingleObject(thread->thread, INFINITE);
    
        GetExitCodeThread(thread->thread, &status);
        CloseHandle(thread->event);
        CloseHandle(thread->thread);
    
        thread->event = NULL;
        thread->thread = NULL;
    
        return status;
    }
    

这组函数应该在StartThread()启动的线程中使用:

  • IsThreadStopped() - 检查终止请求。等待以下功能后必须使用,以确定等待状态终止的实际原因。
  • ThreadSleep() - 替换Sleep()用于线程内代码。
  • ThreadWaitForSingleObject() - 替换使用WaitForSingleObject()作为线程内代码。
  • ThreadWaitForMultipleObjects() - 替换使用WaitForMultipleObjects()作为线程内代码。

第一个功能可用于在长时间运行的作业处理期间对终止请求进行轻量级检查。 (例如大文件压缩)。 其余的函数处理等待某些系统资源的情况,例如事件,信号量等。(例如工作线程等待从请求队列到达的新请求)。

BOOL IsThreadStopped(struct Thread* CONST thread)
{
    return thread->stop;
}

VOID ThreadSleep(struct Thread* CONST thread, DWORD dwMilliseconds)
{
    WaitForSingleObject(thread->event, dwMilliseconds);
}

DWORD ThreadWaitForSingleObject(struct Thread* CONST thread, HANDLE hHandle, DWORD dwMilliseconds)
{
    HANDLE handles[2] = {hHandle, thread->event};

    return WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds);
}

DWORD ThreadWaitForMultipleObjects(struct Thread* CONST thread, DWORD nCount, CONST HANDLE* lpHandles, DWORD dwMilliseconds)
{
    HANDLE* handles = (HANDLE*)malloc(sizeof(HANDLE) * (nCount + 1U));
    DWORD status;

    memcpy(handles, lpHandles, nCount * sizeof(HANDLE));

    handles[nCount] = thread->event;
    status = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds);

    free(handles);

    return status;
}