优雅地终止一个线程不使用TerminateThread()

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

请在这里需要代码。

Solutions Collecting From Web of "优雅地终止一个线程不使用TerminateThread()"

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得到一个错误并退出,但我不会推荐这种方法。

既然你不知道线程在做什么,没有办法从外面安全地终止线程。

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

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

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

你在做什么后台线程? 如果你正在循环一些东西,你可以通过让一个共享的公共静态对象(比如一个Boolean )在前台线程中设置为true ,而后台线程在设置为true时检查并退出。

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

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

  #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; }