Win32线程——在某个线程内终止另一个正在运行的线程(1)
2016-12-16 17:35
260 查看
《Win32多线程程序设计》–Jim Beveridge & Robert Wiener
对于多线程程序,如果用户企图结束程序(主线程)时,子线程尚未完成工作,怎么办?
结束一个线程,听起来好容易,但是结束程序必须按次序进行,以避免发生 race conditions。让程序依次序进行是非常重要的,特别是在程序要结束之前。结束一个程序就好像拆除一栋建筑物一样,在你以推土机轧平它之前,你必须确定每一个人都安全离开了屋子。结束一个程序也是这样,每一个线程都被迫结束,不管它进行到哪里。
TerminateThread() 强迫其行动目标(一个线程)结束,手段激烈而有力,甚至不允许该线程有任何“挣扎”的机会。
此函数唯一可以预期并依恃的是:线程 handle 将变成激发状态,并且传回dwExitCode 所指定的结束代码。
副作用:
1.如果目标线程持有着一个临界区(critical section),这临界区将不会被释放。
2.如果目标线程正在堆里分配内存,堆锁(heap lock)将不会被释放。
3.如果目标线程在结束时调用了某些kernel32,会造成kernel32的状态不一致。
4.如果目标线程正在更改一个共享DLL的全局状态,这个共享DLL的状态可能会被破坏,影响到其他的正在使用这个DLL库的线程。
(范例:经常会出现程序卡死,不能退出的情况)
(某次正常的退出结果)
这个点子似乎不错,因为 C runtime library 支持标准的 signals,如SIGABRT 和 SIGINT。各种 signals 的处理函数可以利用 C 函数 signal() 设立之。
但是我很快就进入了一个死胡同。C runtime 函数中没有一个名为 kill(),而那是 Unix 系统藉以送出 signal 的操作。是有一个 raise() 啦,但只能够传
送 signal 给目前的线程。
观察过 C runtime library 的源代码之后,我发现 signals 其实是利用Win32 的异常情况(exceptions)模拟的,Win32 之中并没有真正的 signals,所以这个想法也行不通。
经过数个小时的努力之后,我可以很确定地告诉各位, Win32 API 中没有什么标准方法可以把一个异常情况丢到另一个线程中。
解决上述缺点:不是写一个 busy loop 来检验标记值,而是使用一个手动重置(manual-reset)的 event 对象。 Worker 线程可以检查该 event 对象的状态或是等待它,视情况而定。
Event对象范例参考:Win32线程——在某个线程内终止另一个正在运行的线程(2)(Event对象)
对于多线程程序,如果用户企图结束程序(主线程)时,子线程尚未完成工作,怎么办?
结束一个线程,听起来好容易,但是结束程序必须按次序进行,以避免发生 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对象)
相关文章推荐
- Win32线程——在某个线程内终止另一个正在运行的线程(2)(Event对象)
- 安全的终止正在运行的线程
- 安全的终止正在运行的线程
- 安全的终止正在运行的线程
- 安全的终止正在运行的线程
- /var/run/yum.pid 已被锁定,PID 为 3021 的另一个程序正在运行
- 终止正在执行的线程
- 【转】Windows终止线程运行的四种方法
- java中,如何安全的结束一个正在运行的线程?
- linux 寻找出当前正在运行的进程以及线程(一条命令)
- OpenCV中如何通过键盘终止一个正在运行的视频
- -终止线程的运行
- java程序中,如何安全的结束一个正在运行的线程?
- 批处理运行遇到“另一个程序正在使用此文件,进程无法访问”
- 解决:无法终止无法挂起BizTalk正在运行的服务实例的问题
- java中用什么方式可以是运行的线程终止????
- windows笔记-终止线程的运行
- 如何终止线程的运行(C/C++)
- 关于c# 中“正在终止线程”的问题
- java中,如何安全的结束一个正在运行的线程?