与信号量、互斥等内核对象比起来线程内核比较龌龊,它在创建时具有2次计数。在《windows核心编程》中说过,如果不需要线程的句柄则可以关闭该句柄(这时递减一次计数),线程在自然结束时还会自动递减一次计数,这样线程内核对象就被系统回收了。今天,我在写多线程时,发现了许多应该注意的问题。
一、我在这关闭线程句柄为什么不行?
for (int i = 0; i < 10; ++i)
{
HANDLE thread = (HANDLE)::_beginthreadex(Null, 0, &WorkerThread, param, 0, 0);
::CloseHandle(thread); // !!!!!!!!!!!!!!!!!!!!
}
哈哈,看出什么问题了吗?按照上面说的线程内核对象具有2次计数,在这里关闭该句柄照逻辑应该没什么事,但事实上是不行的。这里关闭的句柄是【进程相关的】引用计数,因此再次调用_beginthreadex返回的句柄有可能还是上次分配的句柄。意外吧,吃惊吧。知道怎么做了吧?对就是把关闭句柄放在另外一个循环中。
二、不是线程内核对象没有回收吗,怎么不能用了?
如果m_threads是一个已被关闭的线程句柄数组,那么下面的调用会失败。
::WaitForMultipleObjects(m_init, m_threads, TRUE, INFINITE);
线程句柄如果被关闭,那么和【进程相关的】引用计数被递减,如果递减到【进程相关的引用计数为0】,这时m_threads都是无效句柄(可能系统已经把进程的句柄表给清除了)。但是线程内核对象还是存活的,它还有一次引用计数。可怕吧?颤抖了吧?只有线程自然终止或者强制终止TerminateThread,线程内核对象才会被系统回收。