异步过程调用(APC -- Asynchronous Procedure Call )是一种与常用的和简单的同步对象不同的一种同步机制。
我们在我们线程里使用基本的同步对象如MUTEX去通知其它线程,它应该停下来等我们完成之后你再继续。APCs使用了一种不同的策略,
因此可能要求应用程序不同的设计。一个APC是一种回调函数类型。与普通的函数指针在调用上有些类似。我们能够在需要通知其它任务一
些事情时可以调用这样的函数。这个任务将在回调函数内部处理这个事件,而不是等待该事件并在通知后继续工作,实际的工作写在了回调
函数里。普通回调函数将在调用者上下文中执行该函数。例如,线程A调用回调函数,则回调函数运行在线程A的上下文中,同时线程B也正
在做一些事情。想一下,一个线程正在重画窗体,而另外一个线程有一些新的信息要显示在窗口上。
这里的APC机制解决了同步问题,而不需要MUTEX或者Event来包含共享资源。当一个线程使用APC时它告诉系统让其他线程调用该回调函
数时运行在回调函数自己上下文中。换句话说,任务A告诉任务B去执行一个函数。任务B将不中断它自己的工作区执行这个动作。相反它将
完成所有的事情,然后去执行该回调函数。这种同步假设了回调函数将在任务完成之后才被调用,所以它已经完成了资源的使用。看一下画
图的这个例子,它完成了画整个窗体后才开始更新新数据。
APC的机制将等待目标任务完成它的所有工作。因为今天的应用程序大部分时间是在等待下一个系统事件,它假设当任务完成了所有工作,
然后进入一个等待状态知道下一系统事件到来。操作系统(同步库)将偷走这个任务的事件等到该任务进入等待状态时,并跳到了回调函数
的地址上。当函数返回时,该任务将回来在原始的代码或函数上继续等待。
使用同步机制要求在操作期间不等待,所以我们在处理资源时不需要使用MUTEX或事件。
APC是一个在特定线程中执行的函数。当一个APC压入APC队列时,系统将引发一个软中断。接下来该线程将被调度,并运行该APC函数。
APC有2中类型:内核模式的APC和用户模式的APC;内核模式APC由系统产生,而用户模式的由应用程序产生。
每一个线程都有自己的APC队列。可以使用QueueUserAPC函数把一个APC函数压入APC队列中。APC排队就是请求线程调用APC函数。
当用户模式的APC压入线程APC队列后,该线程并不直接调用APC函数,除非该线程是处于可通知状态。当一个线程调用SleepEx,
SignalObjectAndWait、MsgWaitForMultipleObjectsEx,WaitForMultipleObjectsEx或者WaitForSingleObjectEx函数时,它才进入可
通知状态。如果在APC压入队列之前,线程进入可通知状态,则该APC函数将不被执行,因为此时该线程并不是处于可通知状态。然而,该
APC函数仍然在队列中,所以在下次调用可通知状态函数时,它将被调用。
ReadFileEx,SetWaitableTime,SetWaitableTimerEx和WriteFileEx函数是使用一个作为完成通知回调机制的APC来实现的。
如果你正在使用一个线程池,那么注意该APC并不能和其它信号机制一起工作。因为系统控制了线程池的生命周期,可能在通知之前该线程
被终止了。应该使用可等待对象比如CreateThreadpoolTimer创建的定时器,而不是基于APC的通知机制--如SetWaitableTimer或
SetWaitableTimerEx的参数pfnCompletionRoutine。对于I/O,使用一个用CreateThreadpoolIo创建的I/O完成对象或者一个基于事件的
OVERLAPPED结构体传递到SetThreadpoolWait函数中。
当一个发起I/O请求时,一个结构体被分配来表示该请求。该结构体称为I/O请求包(IRP -- I/O request packet)。同步I/O,线程将构造
这个IRP,并发送到设备栈,等待内核完成IRP。异步I/O,线程构造IRP并发送个设备栈。设备栈可能立即完成IRP,也可能返回一个
pending状态,告诉线程正在处理中。这时,该IRP仍然关联着线程,所以该线程终止时或者调用CancelIo时该IRP将被取消。同时,线程在
处理设备栈的IRP时能够继续执行其它任务。
有以下几种方法知道IRP已经完成:
当IRP完成时将用操作结果更新overlapped结构体,所以线程能够轮询操作是否完成。
当IRP完成时将触发overlapped结构体中的event时,所以当操作完成时,线程能同步并处理。
把IRP压入线程未决的APC时,当线程处于可通知状态时将执行APC函数并返回等待操作带有指示它执行一个或多个APC函数的状态。
把IRP压入一个I/O完成端口时,它将被等待该完成端口的下一个线程执行。
等待IO完成端口的线程并不等待可通知状态。因此,如果这些线程发起了被作为APC设置为完成的IRP到线程时,这些IPC完成不能及时地发
生;它们将在线程从IO完成端口获得请求并碰巧进入了可通知状态时才发生。
http://asyncop.com/link.aspx?apc
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681951%28v=vs.85%29.aspx