异步过程调用

异步过程调用(APC)是在特定线程的上下文中异步执行的函数。 当 APC 排队到线程时,系统将发出软件中断。 下次计划线程时,它将运行 APC 函数。 系统生成的 APC 称为 内核模式 APC。 应用程序生成的 APC 称为 用户模式 APC。 线程必须处于可警报状态才能运行用户模式 APC。

每个线程都有自己的 APC 队列。 应用程序通过调用 QueueUserAPC 函数将 APC 排队到线程。 调用线程指定调用 QueueUserAPC中的 APC 函数的地址。 APC 的队列是线程调用 APC 函数的请求。

当用户模式 APC 排队时,不会将排队的线程定向到该线程调用 APC 函数,除非它处于可警报状态。 当线程调用 SleepExSignalObjectAndWaitMsgWaitForMultipleObjectsExWaitForMultipleObjectsExWaitForSingleObjectEx 函数时,线程将进入可警报状态。 如果在 APC 排队之前满足等待,则线程不再处于可警报的等待状态,因此不会执行 APC 函数。 但是,APC 仍处于排队状态,因此当线程调用另一个可警报的等待函数时,将执行 APC 函数。

ReadFileExSetWaitableTimerSetWaitableTimerExWriteFileEx 函数使用 APC 作为完成通知回调机制来实现。

如果使用 线程池,请注意,APC 不起作用以及其他信号机制,因为系统控制线程池线程的生存期,因此在传递通知之前,线程可能会终止。 使用 CreateThreadpoolTimer创建的计时器等 SetWaitableTimerSetWaitableTimerEx,而不是使用基于 APC 的信号机制(如 pfnCompletionRoutine 参数)。 对于 I/O,请使用通过 CreateThreadpoolIo 创建的 I/O 完成对象或基于 hEventOVERLAPPED 结构,在该结构中可以将事件传递给 SetThreadpoolWait 函数。

同步内部

发出 I/O 请求时,会分配一个结构来表示请求。 此结构称为 I/O 请求数据包(IRP)。 使用同步 I/O,线程将生成 IRP,将其发送到设备堆栈,并在内核中等待 IRP 完成。 使用异步 I/O,线程将生成 IRP 并将其发送到设备堆栈。 堆栈可能立即完成 IRP,或者可能会返回指示请求正在进行的挂起状态。 发生这种情况时,IRP 仍与线程关联,因此,如果线程终止或调用函数(如 CancelIo),则会将其取消。 同时,线程可以继续执行其他任务,同时设备堆栈继续处理 IRP。

系统可以通过多种方式指示 IRP 已完成:

  • 使用作结果更新重叠结构,以便线程可以轮询以确定作是否已完成。
  • 在重叠结构中向事件发出信号,以便线程可以在事件上同步,并在作完成时被唤醒。
  • 将 IRP 排入线程挂起的 APC,以便线程在进入可警报的等待状态时执行 APC 例程,并从等待作返回,状态指示它执行了一个或多个 APC 例程。
  • 将 IRP 排到 I/O 完成端口,该端口将由等待完成端口的下一个线程执行。

在 I/O 完成端口上等待的线程不会处于可警报状态等待。 因此,如果这些线程发出设置为作为 APC 完成的 IRP,则这些 IPC 完成不会及时发生:仅当线程从 I/O 完成端口获取请求,然后恰好进入可警报等待时,才会发生这些请求。

在异步过程调用中使用可等待计时器