4.2.7 线程的结束
线程的结束有两种方式。
(1)线程执行完功能函数,自然结束;
(2)被其他线程调用TerminalKernelThread函数强行终止。
其中,第一种结束情况属正常情况,在这种情况下,不会发生资源泄漏等情况,而在第二种情况下,被结束线程申请的系统资源可能得不到释放,从而造成资源的消耗。因此,一般情况下,不建议采用第二种方式结束一个线程。
上文中多次提到,一个新创建的线程刚开始被调度投入运行的时候,是从KernelThreadWrapper函数开始运行的。该函数的上半部分在4.2.4节中有介绍,在本节中,我们重点关注该函数的下半部分,因为这是线程的结束部分。
下面是该函数的相关代码,为了便于阅读,我们分段解释。
static VOID KernelThreadWrapper(__COMMON_OBJECT* lpKThread) { __KERNEL_THREAD_OBJECT* lpKernelThread =NULL; __KERNEL_THREAD_OBJECT* lpWaitingThread =NULL; __PRIORITY_QUEUE* lpWaitingQueue =NULL; __PRIORITY_QUEUE* lpReadyQueue =NULL; DWORD dwRetValue =0L; DWORD dwFlags =0L; ... ... ... dwRetValue= lpKernelThread->KernelThreadRoutine(lpKernelThread->lpRoutineParam );
上述代码中,黑体部分调用了线程的功能函数,在线程从功能函数返回的时候并没有结束,而是继续执行以下代码。
//ENTER_CRITICAL_SECTION(); __ENTER_CRITICAL_SECTION(NULL,dwFlags); lpKernelThread->dwReturnValue =dwRetValue; //Set the return value of this thread. lpKernelThread->dwThreadStatus =KERNEL_THREAD_STATUS_TERMINAL; //Change the status. //LEAVE_CRITICAL_SECTION(); __LEAVE_CRITICAL_SECTION(NULL,dwFlags);
执行完功能函数后,该函数首先设置线程核心对象的返回值,以及线程状态(TERMINAL)。
// //The following code wakeup all kernel thread(s) who waiting for this //kernel thread object. // lpWaitingQueue=lpKernelThread->lpWaitingQueue; lpReadyQueue =KernelThreadManager.lpReadyQueue; lpWaitingThread = (__KERNEL_THREAD_OBJECT*)lpWaitingQueue-> GetHeaderElement((__COMMON_OBJECT*)lpWaitingQueue, NULL); while(lpWaitingThread) { lpWaitingThread->dwThreadStatus=KERNEL_THREAD_STATUS_READY; lpReadyQueue->InsertIntoQueue((__COMMON_OBJECT*)lpReadyQueue, (__COMMON_OBJECT*)lpWaitingThread, lpWaitingThread->dwScheduleCounter); lpWaitingThread = (__KERNEL_THREAD_OBJECT*)lpWaitingQueue-> GetHeaderElement( (__COMMON_OBJECT*)lpWaitingQueue, NULL); }
上述代码唤醒所有等待当前核心线程对象的其他线程。核心线程对象本身也是一个同步对象,其他线程可以等待核心线程对象。一旦核心线程对象的状态被设置为Terminal,所有等到该对象的其他线程将被激活(类似EVENT对象的SetEvent调用)。上述代码就是用来激活所有等待该核心线程对象的其他线程的,这部分代码的详细含义,请参考7.10节。
__TERMINAL: KernelThreadManager.lpTerminalQueue->InsertIntoQueue((__COMMON_ OBJECT*)KernelThreadManager.lpTerminalQueue, (__COMMON_OBJECT*)lpKernelThread, 0L); //Insert the current kernel thread object into TERMINAL queue.
上述代码把当前线程核心对象插入终止队列(lpTerminalQueue)。下面的代码,从就绪队列(lpReadyQueue)中,提取一个就绪线程,并切换到就绪线程,这样当前线程宣告正式结束。需要注意的是,此后当前线程由于不会出现在就绪队列,因此永远得不到调度。处于结束队列(lpTerminalQueue)的核心线程对象,在合适的时机,将会被系统删除。
// //The following code fetch the first READY kernel thread from Ready //Queue,restore it's context,and switch to this kernel thread to continue running. // lpKernelThread=(__KERNEL_THREAD_OBJECT*)KernelThreadManager. lpReadyQueue->GetHeaderElement( (__COMMON_OBJECT*)KernelThreadManager.lpReadyQueue, NULL); if(NULL==lpKernelThread) //If this condition is occurs,the system will crash. { PrintLine("In KernelThreadWrapper."); PrintLine(lpszCriticalMsg); return; } //ENTER_CRITICAL_SECTION(); __ENTER_CRITICAL_SECTION(NULL,dwFlags); KernelThreadManager.lpCurrentKernelThread = lpKernelThread; //!-!-!-!-!-!-!-!-!-!-!-! SwitchTo(&lpKernelThread->KernelThreadContext); //Switch to the new kernel thread. return; //***** CAUTION! ***** : This instruction will never reach. }
上述代码完成当前线程和就绪线程的切换,在SwitchTo函数被调用后,当前的执行线索将转移到从就绪队列提取的就绪线程,SwitchTo函数不会返回,从而最后一条指令(return)永远得不到执行。需要注意的是,__ENTER_CRITICAL_SECTION(NULL, dwFlags)宏被调用,但在本函数中,并没有一个__LEAVE_CRITICAL_SECTION(NULL, dwFlags)与之对应,实际上,__LEAVE_CRITICAL_SECTION宏的功能在SwitchTo函数中已经做了实现。详细的切换信息,请参考4.2.11节。