Ожидание асинхронного ответа
То, что делает клиент, ожидая получения уведомления об ответе с сервера, зависит от выбранного механизма уведомления.
Если клиент использует событие для уведомления, он обычно вызывает функцию WaitForSingleObject или WaitForSingleObjectEx . Клиент переходит в состояние блокировки при вызове любой из этих функций. Это эффективно, так как клиент не использует циклы выполнения ЦП, пока он заблокирован.
При использовании опроса для ожидания результатов клиентская программа входит в цикл, который многократно вызывает функцию RpcAsyncGetCallStatus. Это эффективный метод ожидания, если клиентская программа выполняет другую обработку в цикле опроса. Например, он может подготавливать данные небольшими блоками для последующего асинхронного вызова удаленной процедуры. После завершения каждого блока клиент может опросить невыполненные асинхронные удаленные вызовы процедур, чтобы узнать, завершен ли он.
Клиентская программа может предоставить асинхронный вызов процедуры (APC), который является типом функции обратного вызова, которую библиотека времени выполнения RPC будет вызывать после завершения асинхронного вызова удаленной процедуры. Клиентская программа должна находиться в состоянии ожидания с возможностью предупреждения. Обычно это означает, что клиент вызывает функцию API Windows, чтобы поместить себя в заблокированное состояние. Дополнительные сведения см. в разделе Асинхронные вызовы процедур.
Примечание
Уведомление о завершении не будет возвращено из асинхронной подпрограммы RPC, если во время асинхронного вызова возникает исключение RPC.
Если клиентская программа использует порт завершения ввода-вывода для получения уведомлений о завершении, она должна вызвать функцию 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);
}
В этом случае клиентская программа переходит в спящий режим, не потребляя циклы ЦП, пока библиотека времени выполнения RPC не вызовет APC (не отображается).
В следующем примере показан клиент, который использует порт завершения ввода-вывода для ожидания асинхронного ответа.
// 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 ожидает неограниченное время, пока не завершится асинхронный вызов удаленной процедуры.
При написании многопоточных приложений возникает одна потенциальная ошибка. Если поток вызывает удаленный вызов процедуры, а затем завершает работу до получения уведомления о завершении отправки, удаленный вызов процедуры может завершиться ошибкой, а заглушка клиента может закрыть подключение к серверу. Поэтому потоки, вызывающие удаленную процедуру, не должны завершаться до завершения вызова или отмены, если поведение нежелательно.