等待函数
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 函数等待目标地址的值从某些意外值更改为任何其他值。 这使线程可以等待值更改,而无需旋转或处理线程捕获意外值但线程可以等待之前的值更改时可能出现的同步问题。
WaitOnAddress 返回修改目标值的代码通过调用 WakeByAddressSingle 来唤醒单个等待线程或 WakeByAddressAll 来唤醒所有等待线程来发出更改信号。 如果使用 WaitOnAddress 指定超时间隔,并且没有线程调用唤醒函数,则函数在超时间隔过后返回。 如果未指定超时间隔,线程将无限期等待。
等待函数和超时间隔
指定的超时间隔的准确性取决于系统时钟的分辨率。 系统时钟以固定速率“时钟刻度”。 如果超时间隔小于系统时钟的分辨率,则等待时间可能会少于指定的时间长度。 如果超时间隔大于一个刻度,但小于 2 个周期,则等待时间可以是一到两个周期之间的任意位置,等等。
若要提高等待函数超时间隔的准确性,请调用 timeGetDevCaps 函数来确定支持的最小计时器分辨率和 timeBeginPeriod 函数,将计时器分辨率设置为其最小值。 在调用 timeBeginPeriod时要小心,因为频繁的呼叫可能会显著影响系统时钟、系统电源使用情况和计划程序。 如果调用 timeBeginPeriod,请在应用程序中提前调用一次,并确保在应用程序的末尾调用 timeEndPeriod 函数。
等待函数和同步对象
等待函数可以修改某些类型的 同步对象的状态。 仅针对其信号状态导致函数返回的对象或对象进行修改。 等待函数可以修改同步对象的状态,如下所示:
- 信号灯对象的计数减少一个,如果信号灯的计数为零,信号灯的状态将设置为非对齐。
- 互斥体、自动重置事件和更改通知对象的状态设置为非对齐。
- 同步计时器的状态设置为非对齐状态。
- 手动重置事件、手动重置计时器、进程、线程和控制台输入对象的状态不受等待函数的影响。
等待函数和创建 Windows
使用直接或间接创建窗口的等待函数和代码时,必须小心。 如果线程创建任何窗口,则必须处理消息。 消息广播将发送到系统中的所有窗口。 如果有一个线程使用没有超时间隔的等待函数,系统将死锁。 间接创建窗口的两个代码示例是 DDE 和 CoInitialize 函数。 因此,如果你有一个创建窗口的线程,请使用 MsgWaitForMultipleObjects 或 MsgWaitForMultipleObjectsEx,而不是其他等待函数。