嵌入式操作系统
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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节。