win32 线程知识点梳理三
来源:互联网 发布:windows linux 多线程 编辑:程序博客网 时间:2024/06/05 18:08
本节整理的内容为:如何用一个线程控制另一个正在运行的线程?
方案一:终止一个线程,使用TerminateThread
BOOL WINAPI TerminateThread( _Inout_ HANDLE hThread, _In_ DWORD dwExitCode);
参数1:想要结束的线程的handle
参数2:该线程的结束代码
返回值:
如果函数成功,那么传回True,如果失败,那么返回false
看起来TerminateThread()是不错的,它以强硬的作风迫使目标线程结束,甚至不允许该线程有任何挣扎的机会,这同样也带来了很多副作业,线程没有机会在结束前清理自己。
- 目标线程的堆栈可能没有被释放掉,这就引起了内存泄漏的问题。
- 而且,任何一个和这个线程有关系的DLL都没有机会获得“线程解除附着”的通知。
- 这个函数,同时还带来了另一个隐忧,如果线程进入critical section之中,那么该critical section将因此永远处于锁定状态,因为critical section不像mutex那样,有abandoned的状态。
- 如果目标线程正在更新一份数据结构,这份数据结构也将永远处于不稳定的状态。
得出的结论是:离TerminateThread()远远的!
方案二:使用信号
这个点子似乎是不错的,因为C runtime library 支持标准的signals,如SIGABRT和SIGINT,各种signals处理函数可以利用C函数的signal() 设立之。
但是在C runtime函数中没有kill(),那是Unix系统借以送出signal的操作。虽然有一个raise(),但是只能够传送signal给目前的线程。
本质上,signals就是利用Win32的异常exceptions模拟的,Win32中没有真正的signals
所以这个方案是行不通的。
方案三:Exceptions
在目标线程中引发一个异常情况,如果有必要在结束前清理某些东西,目标线程可以设法捕捉这一异常情况,否则它可以什么都不管的终结自己的生命。
win32 API中没有标准的方法。
可以采用的技术是:利用debugging API写一些不合法指令到目标线程的目前地址上
另一种做法是改变一个常用的指针,使它指向一个不合法地址,因而强迫程序代码产生一个异常情况。
方案四:设立一个标记
win32核准的标准做法是,设立一个标记,利用其值来要求线程结束自己。
这种做法有一个明确的缺点,线程需要一个polling机制,时时检查标记值,以决定该不该结束自己。
具体做要的事情就是使用一个manual-reset的event对象,worker线程可以检查该event对象的状态或者等待它。
原书这个部分有一个测试代码。我在win32平台上测试了一下。
#define WIN32_LEAN_AND_MEAN // reduce the size of the Win32 header files#include <stdio.h>#include <stdlib.h>#include <windows.h>#include <time.h>HANDLE hRequestExitEvent = false;DWORD WINAPI ThreadFunc(LPVOID p){ int i ; int inside = 0; UNREFERENCED_PARAMETER(p); srand((unsigned)time(NULL)); for(i=0;i<10000000;i++){ //这里修改为原来的10倍,视机器而定,循环有可能过快执行完 double x = (double)(rand())/RAND_MAX; double y = (double)(rand())/RAND_MAX; if((x*x+y*y)<=1.0) inside++; if(WaitForSingleObject(hRequestExitEvent,0)!=WAIT_TIMEOUT){ printf("received request to terminate\n"); return (DWORD)-1; } } printf("PI = %.4g\n",(double)inside/i*4); return 0;}int main(){ HANDLE hThreads[2]; DWORD dwThreadId; DWORD dwExitCode = 0; int i ; hRequestExitEvent = CreateEvent(NULL,true,false,NULL);//manual reset ,非激发状态 for(i=0;i<2;i++){ hThreads[i] = CreateThread(NULL,0,ThreadFunc,(LPVOID)i,0,&dwThreadId); } Sleep(1000); SetEvent(hRequestExitEvent);//设为激发状态 WaitForMultipleObjects(2,hThreads,true,INFINITE);//所有handle都激发才能返回 for(i=0;i<2;i++) CloseHandle(hThreads[i]); return EXIT_SUCCESS;}
运行可以看到提示。
这里,用event对象来取代一个简单的全局变量,这个例子中并不一定需要event对象,但如果我们使用它,worker线程可以在必要的时候等待之。
例如worker线程可以利用event对象来等待一个Internet socket连接成功,或者等待用户发出离开的请求。
另外,在main中等待所有线程的handles变为激发状态,可以保证线程都已安全地离开了。
商业软件中,可能不会用到无穷等待,而会设定一个上限值,例如15秒或30秒,通常那足以表明线程已经失去了反应。
资料来源:
win32多线程程序设计
- win32 线程知识点梳理三
- Win32 线程知识点梳理一
- Win32 线程知识点梳理二
- win32 线程知识点梳理四
- Win32 线程知识点梳理五
- win32 多线程知识点梳理六 IOCP
- 面向对象知识点梳理(三)
- 进程和线程--重磅推出--知识点梳理
- QT5知识点记录梳理(三)动作
- 计算机组成原理知识点梳理(三)
- 梳理shell编程遗忘的知识点笔记(三)
- JAVA基础知识点梳理三:流程控制语句
- 软件设计师知识点梳理
- 软件设计师知识点梳理
- 函数模板知识点梳理
- 类模板知识点梳理
- 博客中知识点梳理
- Memcache知识点梳理(转)
- Linux操作系统上的文件共享
- Majority Element II 寻找数组中出现次数大于n/3的数
- 运算优先级等几个小问题总结
- python多继承(新式类)一
- SQL Sever2008表格的增删改查
- win32 线程知识点梳理三
- android4.x 增加一个新按键&&修改android默认语言
- java中文件的创建
- Light oj 1138 - Trailing Zeroes (III)
- Collections.sort
- 关于预处理#
- 冒泡排序
- iOS数据请求之SQLite
- 便利构造器/代码块