您的位置:首页 > 其它

Win32线程——在某个线程内终止另一个正在运行的线程(1)

2016-12-16 17:35 260 查看
《Win32多线程程序设计》–Jim Beveridge & Robert Wiener

对于多线程程序,如果用户企图结束程序(主线程)时,子线程尚未完成工作,怎么办?

结束一个线程,听起来好容易,但是结束程序必须按次序进行,以避免发生 race conditions。让程序依次序进行是非常重要的,特别是在程序要结束之前。结束一个程序就好像拆除一栋建筑物一样,在你以推土机轧平它之前,你必须确定每一个人都安全离开了屋子。结束一个程序也是这样,每一个线程都被迫结束,不管它进行到哪里。

做法一:在任何子线程还没完成其工作之前,不准用户结束程序(可能需要长时间等待)

缺点:对于GUI程序,可能需要很长的时间等待程序退出,那么程序有可能看起来像是“挂”了一样。

做法二:TerminateThread() 放弃一个线程(微软不建议使用)

缺点:

TerminateThread() 强迫其行动目标(一个线程)结束,手段激烈而有力,甚至不允许该线程有任何“挣扎”的机会。

此函数唯一可以预期并依恃的是:线程 handle 将变成激发状态,并且传回dwExitCode 所指定的结束代码。

副作用:

1.如果目标线程持有着一个临界区(critical section),这临界区将不会被释放。

2.如果目标线程正在堆里分配内存,堆锁(heap lock)将不会被释放。

3.如果目标线程在结束时调用了某些kernel32,会造成kernel32的状态不一致。

4.如果目标线程正在更改一个共享DLL的全局状态,这个共享DLL的状态可能会被破坏,影响到其他的正在使用这个DLL库的线程。

(范例:经常会出现程序卡死,不能退出的情况)

#include <stdio.h>
#include <time.h>
#include <Windows.h>

DWORD WINAPI Thread(void *arg) {
while (1) {
printf("Run #%d\n", (int)time(NULL));
Sleep(1000);
}
return 0;
}

int main(void) {
HANDLE hThread = CreateThread(NULL, 0, Thread, NULL, 0, NULL);

Sleep(3000); // 3s后强制结束线程hTread,线程退出码为12345
TerminateThread(hThread, 12345);

DWORD exitCode;
while (1) {
GetExitCodeThread(hThread, &exitCode); // 严重浪费 CPU 时间
printf("exitCode=%lu\n", exitCode);
if (STILL_ACTIVE != exitCode)
break;

}

CloseHandle(hThread);
return 0;
}


(某次正常的退出结果)



做法三:使用信号(Signals)(行不通)

在Unix系统中,signals 是跨进程传送通告(notifications)的标准方法,SIGTERM 相当于“请你离开”的意思,SIGKILL 则是粗略相当于 TerminateThread()。

这个点子似乎不错,因为 C runtime library 支持标准的 signals,如SIGABRT 和 SIGINT。各种 signals 的处理函数可以利用 C 函数 signal() 设立之。

但是我很快就进入了一个死胡同。C runtime 函数中没有一个名为 kill(),而那是 Unix 系统藉以送出 signal 的操作。是有一个 raise() 啦,但只能够传

送 signal 给目前的线程。

观察过 C runtime library 的源代码之后,我发现 signals 其实是利用Win32 的异常情况(exceptions)模拟的,Win32 之中并没有真正的 signals,所以这个想法也行不通。

做法四:跨越线程,丢出异常情况(Exceptions)(Win32 API不支持)

在目标线程中引发一个异常情况(exception)。如果有必要在结束前清理某些东西,目标线程可以设法捕捉此一异常情况,否则它可以什么都不管地直接结束自己的生命。

经过数个小时的努力之后,我可以很确定地告诉各位, Win32 API 中没有什么标准方法可以把一个异常情况丢到另一个线程中

做法五:设立一个标记

缺点线程需要一个 polling 机制,时时检查标记值,以决定该不该结束自己。

解决上述缺点:不是写一个 busy loop 来检验标记值,而是使用一个手动重置(manual-reset)的 event 对象。 Worker 线程可以检查该 event 对象的状态或是等待它,视情况而定。

Event对象范例参考:Win32线程——在某个线程内终止另一个正在运行的线程(2)(Event对象)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: