当前位置:C++技术网 > 资讯 > [Win32] 奇怪的线程句柄操作机制

[Win32] 奇怪的线程句柄操作机制

更新时间:2017-03-07 14:20:41浏览次数:1+次

    我今天想做个简单的线程控制类,然后我就先做个简单的小测试,于是就有了一个奇怪的现象。

    代码如下:


HANDLE g_hThread;

DWORD WINAPI MyThread(LPVOID lpParam)
{
	Sleep(2000);	// 用于等待线程句柄赋值

	BOOL b = CloseHandle(g_hThread);	// 返回值居然是TRUE,而且还关闭了!

	Sleep(3000);	// 用于测试是否还在继续等待

	return 0;
}

int main()
{
	g_hThread = CreateThread(NULL, 0, MyThread, NULL, 0, NULL);

	DWORD dw = WaitForSingleObject(g_hThread, INFINITE);	// 返回值居然是 0!

	_getch();
	return 0;
}
     总结:


    1. 我先创建了一条副线程,并返回了线程句柄。

    2. 然后创建的线程还在睡眠状态,主线程立即进入了 等待线程 退出状态。

    3. 线程醒来后,立刻把线程句柄成功的关闭掉了,然后进入了睡眠状态。

    4. 主线程居然还在等待副线程!

    5. 副线程退出后,主线程也跟着退出了。

    问题:

    1. 我明明把线程句柄,也就是内核对象进行了关闭处理,按道理来说 WaitForSingleObject() 函数应该也就失去了对象的联系才对,为什么还能处于等待状态。

    2. 还是说 CloseHandle() 函数只是在对象结构中做了标志位处理?对象会在没有任何操作之后,系统会自动关闭?

    3. 又或者是说这是系统补丁解决了这个BUG?(本人Win7 64位系统)

    

    最后感谢C++技术网的回答。



C++技术网会员解答:

    你好,感谢对C++技术网的支持与信任。

    你所遇见的问题只是因为对句柄和线程的关系有误解。下面来给你分析一下,供你学习参考。

    首先给你一个参考学习的文章:《关闭进程句柄与进程终止的误解》,这是进程句柄和关闭进程的误解的说明,和你这个问题的线程其实是一个道理。

    然后我再结合你这个问题,来分析一下。

    句柄,是Windows中对资源使用的一个“钥匙”。而实际的资源并不是这个钥匙。所以,关闭句柄,只是干掉了钥匙,并不能干掉钥匙能够开启的资源。我想这样是不是很好理解。

    下面来做一个专业的说明。

    Windows中的各种资源,包含进程、线程、通信端口、文件、位图、内存等各种资源都是可以供使用的。而很多资源都是共享的,比如文件,内存。而这些资源的使用是由操作系统来管理和分配的,而不是谁想用就拿来用。而为了提高效率,共享资源可能被很多人使用,那么系统就没有必要分配很多份,只需要一份资源即可满足大家。比如3个人读取一个文件,只需要将文件载入一次即可。对于进程线程的管理,同样如此。

    而Windows中资源的使用都会在系统中登记,登记表中的序号就是句柄。所以我们通过句柄就可以使用资源,只是因为我们将句柄传给系统后,系统通过句柄可以查找到对应的资源,然后可以操作资源。句柄仅仅是一个数字而已。我们完全可以自己写一个数值,然后强制转换为句柄。但是这样的句柄,不一定有效。你要看这个句柄值有没有在系统中登记,如果没有的登记,那就是非法无效的句柄。

    而共享资源会被多个地方多次使用,或者叫做共同使用共享资源。这样系统会对这个句柄引用的次数进行计数。如果有三个人,那就计数为3,表示有3个人在使用。只要这个计数不为0,资源就不会释放。否则就会导致使用中的人出现异常,操作已经释放的资源而崩溃。

    那么以上就解释了资源、句柄、资源引用计数的概念。下面再进一步细化说明一下进程线程资源的背景知识。

    对于文件这类资源来说,我们可以说这些资源是静态的,是死的。这些资源只是一堆不会动的资源。而进程线程则不一样,进程线程是系统中可以执行可以活动的资源,就像一个生命体。我们将这类资源成为动态的资源。当然,静态和动态资源是我临时的一个叫法,不是专业的叫法,只是为了形象区别两类资源而已。

    静态资源是无法管理自己的,而动态资源是可以管理自己的。一张桌子,是生是死是完全任人摆布的。而动态资源好比一个人的生死,不仅可以任人摆布,而且可以自己摆布自己,比如可以自杀结束生命,也可以逃亡摆脱他人的摆布(病毒就会逃脱系统或杀软的追杀)。而一般的进程执行完,就会自己乖乖的自杀或者再做其他事情(线程池的线程)。文件的打开和关闭,你说关闭,他就关闭了,不会有任何的反抗。

    在Windows中,我们对资源的释放,使用CloseHandle()就可以了。但是是不是就真的释放呢,由系统来管理。如果资源被多次引用,那么你这次释放,对你自己来说是释放了,但是对于资源来说,资源没有释放。你只是释放了你自己对这个资源的使用权。释放之后,你就无法再使用这个资源了。这一点需要很清楚。

    而进程线程在被创建的时候,如果不返回句柄,进程线程作为一种资源,使用次数就有一次了。而这一次就是进程线程自己掌握的。就像我们人类自己的生命,拥有一次自主决定权。如果返回句柄,那么这个资源就被引用了两次。他人可以通过这个句柄来操作这个进程线程。当然就包括干掉进程线程,其他的操作自然不必多说,都可以。

    所以说,你创建线程后,句柄存储为全局的了。所以都可以通过这个句柄来操作线程。当然,线程自己也可以通过这个句柄来操作自己。你在线程函数里面关闭了句柄,达到的效果是:关闭对外的句柄,让其他人无法干预自己了,更不用说干掉自己了,自己就自由了。而不是你要的干掉自己。你关闭句柄后,句柄引用由2变为1,这个1就是线程自己的生命。如果线程不结束,这个句柄引用就不会消失。只要线程一结束,线程作为资源就要被系统清除了。

    所以你在线程函数中,解除了一次线程资源的引用。此时线程资源还有一次引用,所以主线程的等待还会继续。线程即使睡眠,也是活着的,资源一直是使用状态。

    你这次尝试的过程,通过本次分析,应该可以让你对线程和句柄有一个深刻而准确的理解了。