首页 理论教育 WaitForThisObjectEx函数的实现

WaitForThisObjectEx函数的实现

时间:2023-10-21 理论教育 版权反馈
【摘要】:如果把超时参数设置为0,则该函数的行为与WaitForThisObject类似,永远等待,直到资源变得可用。因此,对于超时参数为0的情况,该函数直接调用WaitForThisObject,而对于超时参数不为0的情况,该函数需要完成下列操作:递减nCounter变量,并判断递减结果。该函数完成如下功能:从semaphore对象的等待队列中,把设定该定时器的线程删除,并修改该线程的状态为READY,插入就绪队列,其实是一个唤醒过程。下面是WaitForThisObjectEx函数的实现代码。

WaitForThisObjectEx函数的实现

与WaitForThisObject不同的是,WaitForThisObjectEx支持超时等待,即调用者(通常情况下是一个线程)可以设定一个时间参数,资源如果能够在时间参数超时前(从调用开始计时)可用,则返回资源可用指示,否则,返回定时器超时指示。

如果把超时参数设置为0,则该函数的行为与WaitForThisObject类似,永远等待,直到资源变得可用。因此,对于超时参数为0的情况,该函数直接调用WaitForThisObject,而对于超时参数不为0的情况,该函数需要完成下列操作:

(1)递减nCounter变量,并判断递减结果。

(2)如果递减后的结果大于或者等于0,则说明尚有足够的资源,于是直接返回资源成功获取指示(返回1)。

(3)如果递减后的结果小于0,则说明当前已经没有足够的资源可供使用,于是准备超时等待操作。

(4)首先,该函数设定一个一次性定时器,该定时器的超时参数设置为调用者传递过来的超时参数,并设定一个回调函数,以便在定时器超时时调用。

(5)然后该函数设置当前线程状态为阻塞,并把当前线程插入等待队列。

(6)调用ScheduleFromProc例程,重新调度。

(7)ScheduleFromProc返回后(当前线程重新被激活),判断激活原因是超时,还是已经获得了资源。

(8)如果激活原因是获得了资源,则返回资源获得信息(返回1),否则,返回超时信息SEMAPHORE_TIMEOUT,以指示用户等待超时。

函数调用返回后,调用者需要判断该函数的返回原因(是获得了资源,还是因为超时返回)。如果是因为在超时前获得了请求的资源,则在后续的某个恰当的地方需要执行ReleaseSemaphore,以释放获得的资源;如果是因为超时返回,则不需要调用ReleaseSemaphore函数,因为当前线程从来就没有获得过semaphore资源。

上述描述,提到了一个定时器超时的时候调用的回调函数,该函数定义如下。

一旦定时器超时,该函数就会被调用(在时钟中断上下文中被调用,因此,该函数在实现的时候,一定要短小紧凑,以减少处理时间)。该函数完成如下功能:

(1)从semaphore对象的等待队列中,把设定该定时器的线程(即调用WaitForThis-ObjectEx,并设定超时参数的线程)删除,并修改该线程的状态为READY,插入就绪队列,其实是一个唤醒过程。

(2)设置线程的唤醒原因为超时。

这样被唤醒的线程在合适的时候就会被调度运行。细心的读者可能发现,上述回调函数的处理存在一个问题。假设在定时器没有超时前,有另外一个占有semaphore对象的线程释放了semaphore资源(调用ReleaseSemaphore函数),这样处于等待状态的线程(假设该线程为TA)被唤醒,并插入就绪队列,但还未被调度运行,这个时候定时器超时,即上述事件按照下列时序发生:

(1)另外一个占有semaphore资源的线程释放semaphore资源。

(2)等待semaphore资源(超时等待)的线程(TA)被唤醒,并插入就绪队列,但还未被调度运行。(www.xing528.com)

(3)TA设定的定时器(其实是TA以超时方式调用WaitForThisObjectEx函数,WaitForThisObjectEx参数设定定时器)超时,回调函数在中断上下文中被调用。

(4)TA被重新调度,投入运行。

在上述情形中,回调函数在从Semaphore对象的就绪队列中删除TA的时候,就会失败(因为TA已经不在Semaphore对象的等待队列里面了)。这个时候,说明TA已经获得了Semaphore资源被重新调度,因此,需要设置TA被唤醒的原因为获得了资源,而不应该是超时。

综上所述,回调函数的处理过程应该如下:

(1)调用DeleteFromQueue函数,从semaphore对象的等待队列中删除设定超时等待的线程。

(2)如果DeleteFromQueue返回TRUE,则说明该线程尚未被唤醒,回调函数就设置该线程状态为就绪(KERNEL_THREAD_STATUS_READY),插入就绪队列,并设置线程唤醒原因为超时(SEMAPHORE_WAIT_TIMEOUT)。

(3)如果DeleteFromQueue返回FALSE,说明等待队列中已经不存在TA线程,即TA线程可能已经获得了semaphore资源,早已被唤醒,因此,这个时候,就需要设定返回原因为获得资源(SEMAPHORE_WAIT_RESOURCE),并返回。

另外一种可能的时序,就是:

(1)等待semaphore资源的线程TA,由于获得了资源而被唤醒。

(2)在一个时钟中断内,TA得到调度投入运行,而TA设定的定时器还未超时。

这种情况下,TA投入运行后,第一件事情就是判断被唤醒的原因(得到资源或者超时)。如果是得到资源,则删除定时器对象(因为这个时候,定时器还未超时,定时器对象仍然存在);如果是超时,则说明定时器已经超时,回调函数已经被调用,而且定时器对象已经被删除(一次性定时器在超时后,立即被删除),因此这个时候函数只需要返回即可。

综上所述,semaphore的回调函数实现方式如下:

可以看出,该函数首先试图从等待队列中删除等待线程,如果成功,则设置超时原因,否则不做任何动作,直接返回。

__SEMAPHORE_CALL_BACK结果是一个局部定义的数据结构,用来完成callback函数的参数传递工作,该数据结构包含三个成员:等待线程(lpKernelThread)、线程被唤醒的原因(dwWakeupReason)以及Semaphore对象指针(lpSem)。

下面是WaitForThisObjectEx函数的实现代码。

该函数首先检查是否有足够的可用资源,如果是,则直接返回成功标志(SEMAPHORE_WAIT_RESOURCE),否则,设置定时器,并阻塞当前线程。

需要注意的是,该函数完成lpCallbackParam的内存分配后,把dwWakeupReason初始化为SEMAPHORE_WAIT_RESOURCE,并把lpCallbackParam作为参数传递给SetTimer函数。如果SemCallback被调用,则dwWakeupReason将被修改为TIMEOUT,否则,会一直保持SEMAPHORE_WAIT_RESOURCE。在当前线程被唤醒之后(代码中黑色字体注释部分开始),就可以根据dwWakeupReason的当前值确定不同的动作流程。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈