等待异步答复

客户端在等待收到来自服务器的回复的通知时执行的操作取决于它选择的通知机制。

如果客户端使用事件作为通知,它通常会调用 WaitForSingleObject 函数或 WaitForSingleObjectEx 函数。 客户端在调用任一函数时进入阻止状态。 这是有效的,因为客户端在被阻止时不会消耗 CPU 运行周期。

当它使用轮询来等待其结果时,客户端程序将进入一个循环,该循环重复调用函数 RpcAsyncGetCallStatus。 如果客户端程序在轮询循环中执行其他处理,这是一种有效的等待方法。 例如,它可以为后续异步远程过程调用准备小区块中的数据。 每个区块完成后,客户端可以轮询未完成的异步远程过程调用,以查看它是否已完成。

客户端程序可以 (APC) 提供异步过程调用,这是 RPC 运行时库在异步远程过程调用完成时调用的一种回调函数。 客户端程序必须处于可警报等待状态。 这通常意味着客户端调用 Windows API 函数以将自身置于阻止状态。 有关详细信息,请参阅 异步过程调用

注意

如果在异步调用期间引发 RPC 异常,则不会从异步 RPC 例程返回完成通知。

 

如果客户端程序使用 I/O 完成端口接收完成通知,则必须调用 GetQueuedCompletionStatus 函数。 当它这样做时,它可以无限期地等待响应或继续执行其他处理。 如果在等待回复时执行其他处理,则必须使用 GetQueuedCompletionStatus 函数轮询完成端口。 在这种情况下,它通常需要将 dwMilliseconds 设置为零。 这会导致 GetQueuedCompletionStatus 立即返回,即使异步调用尚未完成。

客户端程序还可以通过其窗口消息队列接收完成通知。 在这种情况下,他们只需处理完成消息,就像处理任何 Windows 消息一样。

在多线程应用程序中,只有在发起调用的线程从调用成功返回后,客户端才能取消异步调用。 这可确保在同步调用失败后,另一个线程不会异步取消调用。 作为标准做法,同步失败的异步调用不应异步取消。 如果可以在不同线程上发出和取消调用,则客户端应用程序必须观察此行为。 此外,取消调用后,客户端代码必须等待完成通知并完成调用。 RpcAsyncCancelCall 函数只是匆忙完成通知;它不能替代完成调用。

以下代码片段演示了客户端程序如何使用事件来等待异步答复。

// This code fragment assumes that Async is a valid asynchronous
// RPC handle.
if (WaitForSingleObject(Async.u.hEvent, INFINITE) == WAIT_FAILED)
{
    RpcRaiseException(APP_ERROR);
}

使用 APC 接收异步答复通知的客户端程序通常会将自己置于阻止状态。 以下代码片段演示了这一点。

if (SleepEx(INFINITE, TRUE) != WAIT_IO_COMPLETION)
{
    RpcRaiseException(APP_ERROR);
}

在这种情况下,客户端程序进入睡眠状态,不消耗 CPU 周期,直到 RPC 运行时库调用 APC (未显示) 。

下一个示例演示使用 I/O 完成端口等待异步答复的客户端。

// This code fragment assumes that Async is a valid asynchronous
// RPC handle.
if (!GetQueuedCompletionStatus(
         Async.u.IOC.hIOPort,
         &Async.u.IOC.dwNumberOfBytesTransferred,
         &Async.u.IOC.dwCompletionKey,
         &Async.u.IOC.lpOverlapped,
         INFINITE))
{
    RpcRaiseException(APP_ERROR);
}

在前面的示例中,对 GetQueuedCompletionStatus 的 调用将无限期等待,直到异步远程过程调用完成。

编写多线程应用程序时,会出现一个潜在的缺陷。 如果线程调用远程过程调用并在收到发送完成的通知之前终止,则远程过程调用可能会失败,并且客户端存根可能会关闭与服务器的连接。 因此,调用远程过程的线程不应在调用完成之前终止,或在行为不可取时取消。