等待函数
等待函数 允许线程阻止其自己的执行。 等待函数在满足指定条件之前不会返回。 wait 函数的类型确定所使用的条件集。 调用等待函数时,它会检查是否已满足等待条件。 如果未满足条件,则调用线程将进入等待状态,直到满足等待条件的条件或指定的超时间隔已过。
单对象等待函数
SignalObjectAndWait、WaitForSingleObject 和 WaitForSingleObjectEx 函数需要一个同步对象的句柄。 发生以下任一情况时,这些函数将返回:
- 指定的对象处于已发出信号的状态。
- 超时间隔已过。 超时间隔可以设置为 INFINITE ,以指定等待不会超时。
SignalObjectAndWait 函数使调用线程能够以原子方式将对象的状态设置为信号,并等待另一个对象的状态设置为信号。
多对象等待函数
WaitForMultipleObjects、WaitForMultipleObjectsEx、MsgWaitForMultipleObjects 和 MsgWaitForMultipleObjectsEx 函数使调用线程能够指定包含一个或多个同步对象句柄的数组。 发生以下任一情况时,这些函数将返回:
- 任何一个指定对象的状态都设置为“已发出信号”,或者所有对象的状态都已设置为“已发出信号”。 你可以控制在函数调用中使用一种还是所有状态。
- 超时间隔已过。 超时间隔可以设置为 INFINITE ,以指定等待不会超时。
使用 MsgWaitForMultipleObjects 和 MsgWaitForMultipleObjectsEx 函数,可以在对象句柄数组中指定输入事件对象。 这是在线程的输入队列中指定要等待的输入类型时完成的。 例如,线程可以使用 MsgWaitForMultipleObjects 阻止其执行,直到指定对象的状态设置为已发出信号,并且线程的输入队列中有可用的鼠标输入。 线程可以使用 GetMessage 、 PeekMessageA 或 PeekMessageW 函数来检索输入。
当等待所有对象的状态设置为信号时,这些多对象函数不会修改指定对象的状态,直到所有对象的状态都已设置信号。 例如,可以向互斥对象的状态发出信号,但在数组中指定的其他对象的状态也设置为信号之前,调用线程不会获得所有权。 同时,其他一些线程可能会获得互斥对象的所有权,从而将其状态设置为非签名。
在等待单个对象的状态设置为信号时,这些多对象函数检查数组中的句柄,从索引 0 开始,直到向其中一个对象发出信号。 如果多个对象被发出信号,函数将返回数组中第一个句柄的索引,该句柄已发出对象信号。
可警报等待函数
MsgWaitForMultipleObjectsEx、SignalObjectAndWait、WaitForMultipleObjectsEx 和 WaitForSingleObjectEx 函数与其他等待函数不同,因为它们可以选择执行可警报的等待操作。 在可发出警报的等待操作中,函数可以在满足指定条件时返回,但如果系统将 I/O 完成例程或 APC 排队等待线程执行,函数也可以返回。 有关可发出警报的等待操作和 I/O 完成例程的详细信息,请参阅 同步和重叠的输入和输出。 有关 APC 的详细信息,请参阅 异步过程调用。
已注册的等待函数
RegisterWaitForSingleObject 函数与其他等待函数的不同之处在于,等待操作由线程池中的线程执行。 满足指定条件时,回调函数由线程池中的工作线程执行。
默认情况下,已注册的等待操作是多等待操作。 每次向事件发出信号时,系统都会重置计时器 (或超时间隔) ,直到调用 UnregisterWaitEx 函数来取消操作。 若要指定等待操作应只执行一次,请将 RegisterWaitForSingleObject 的 dwFlags 参数设置为 WT_EXECUTEONLYONCE。
如果线程调用使用 APC 的函数,请将 RegisterWaitForSingleObject 的 dwFlags 参数设置为 WT_EXECUTEINPERSISTENTTHREAD。
正在等待地址
线程可以使用 WaitOnAddress 函数来等待目标地址的值从某些不需要的值更改为任何其他值。 这使线程能够等待值更改,而无需旋转或处理线程捕获不需要的值但值更改后线程可以等待时可能出现的同步问题。
当修改目标值的代码通过调用 WakeByAddressSingle 来唤醒单个等待线程或调用 WakeByAddressAll 来唤醒所有等待线程来发出更改信号时,WaitOnAddress 将返回。 如果使用 WaitOnAddress 指定了超时间隔,并且没有线程调用唤醒函数,则当超时间隔过后,该函数将返回。 如果未指定超时间隔,则线程将无限期等待。
等待函数和超时间隔
指定超时间隔的准确性取决于系统时钟的分辨率。 系统时钟以恒定速率“滴答” 。 如果超时间隔小于系统时钟的分辨率,则等待可能会少于指定的时间长度。 如果超时间隔大于一个刻度,但小于两个刻度周期,则等待时间可以是介于 1 和 2 个刻度之间的任意位置,依此类比。
若要提高等待函数超时间隔的准确性,请调用 timeGetDevCaps 函数来确定支持的最小计时器分辨率,并调用 timeBeginPeriod 函数将计时器分辨率设置为其最小值。 调用 timeBeginPeriod 时请谨慎,因为频繁调用可能会显著影响系统时钟、系统电源使用情况和计划程序。 如果调用 timeBeginPeriod,请在应用程序早期调用一次,并确保在应用程序最末尾调用 timeEndPeriod 函数。
Wait 函数和同步对象
等待函数可以修改某些类型的 同步对象的状态。 仅针对其信号状态导致函数返回的对象或对象进行修改。 Wait 函数可以修改同步对象的状态,如下所示:
- 信号灯对象的计数将减少一个,如果信号量计数为零,则信号灯的状态将设置为无信号。
- 互斥、自动重置事件和更改通知对象的状态设置为不对齐。
- 同步计时器的状态设置为“未签名”。
- 手动重置事件、手动重置计时器、进程、线程和控制台输入对象的状态不受等待函数的影响。
等待函数和创建窗口
使用直接或间接创建窗口的等待函数和代码时必须小心。 如果线程创建任何窗口,它必须处理消息。 消息广播将发送到系统中的所有窗口。 如果有一个线程使用没有超时间隔的等待函数,系统会死锁。 间接创建窗口的两个代码示例是 DDE 和 CoInitialize 函数。 因此,如果你有创建窗口的线程,请使用 MsgWaitForMultipleObjects 或 MsgWaitForMultipleObjectsEx,而不是其他等待函数。