Aguardando a resposta assíncrona
O que o cliente faz enquanto aguarda para ser notificado de uma resposta do servidor depende do mecanismo de notificação selecionado.
Se o cliente usar um evento para notificação, ele normalmente chamará a função WaitForSingleObject ou a função WaitForSingleObjectEx . O cliente entra em um estado bloqueado quando chama uma dessas funções. Isso é eficiente porque o cliente não consome ciclos de execução de CPU enquanto está bloqueado.
Quando ele usa sondagem para aguardar seus resultados, o programa cliente insere um loop que chama repetidamente a função RpcAsyncGetCallStatus. Esse é um método eficiente de espera se o programa cliente faz outro processamento no loop de sondagem. Por exemplo, ele pode preparar dados em pequenas partes para uma chamada de procedimento remoto assíncrona subsequente. Depois que cada parte for concluída, o cliente poderá sondar a chamada de procedimento remoto assíncrona pendente para ver se ela está concluída.
Seu programa cliente pode fornecer uma APC (chamada de procedimento assíncrono), que é um tipo de função de retorno de chamada que a biblioteca de tempo de execução RPC invocará quando a chamada de procedimento remoto assíncrono for concluída. Seu programa cliente deve estar em um estado de espera alertável. Isso normalmente significa que o cliente chama uma função da API do Windows para se colocar em um estado bloqueado. Para obter mais informações, consulte Chamadas de procedimento assíncrono.
Observação
A notificação de conclusão não será retornada de uma rotina RPC assíncrona se uma exceção RPC for gerada durante uma chamada assíncrona.
Se o programa cliente usar uma porta de conclusão de E/S para receber uma notificação de conclusão, ele deverá chamar a função GetQueuedCompletionStatus . Quando isso acontece, ele pode esperar indefinidamente por uma resposta ou continuar a fazer outro processamento. Se ele fizer outro processamento enquanto aguarda uma resposta, ele deverá sondar a porta de conclusão com a função GetQueuedCompletionStatus . Nesse caso, normalmente, ele precisa definir dwMilliseconds como zero. Isso faz com que GetQueuedCompletionStatus retorne imediatamente, mesmo que a chamada assíncrona não tenha sido concluída.
Os programas cliente também podem receber notificação de conclusão por meio de suas filas de mensagens de janela. Nessa situação, eles simplesmente processam a mensagem de conclusão como fariam com qualquer mensagem do Windows.
Em um aplicativo multithreaded, uma chamada assíncrona só pode ser cancelada pelo cliente depois que o thread que originou a chamada tiver retornado com êxito da chamada. Isso garante que a chamada não seja cancelada de forma assíncrona por outro thread depois que ela falhou em uma chamada síncrona. Como prática padrão, uma chamada assíncrona que falha de forma síncrona não deve ser cancelada de forma assíncrona. O aplicativo cliente deve observar esse comportamento se as chamadas puderem ser emitidas e canceladas em threads diferentes. Além disso, depois que a chamada for cancelada, o código do cliente deverá aguardar a notificação de conclusão e concluir a chamada. A função RpcAsyncCancelCall simplesmente apressa a notificação de conclusão; não é um substituto para concluir a chamada.
O fragmento de código a seguir ilustra como um programa cliente pode usar um evento para aguardar uma resposta assíncrona.
// This code fragment assumes that Async is a valid asynchronous
// RPC handle.
if (WaitForSingleObject(Async.u.hEvent, INFINITE) == WAIT_FAILED)
{
RpcRaiseException(APP_ERROR);
}
Programas cliente que usam um APC para receber uma notificação de uma resposta assíncrona normalmente se colocam em um estado bloqueado. O fragmento de código a seguir mostra isso.
if (SleepEx(INFINITE, TRUE) != WAIT_IO_COMPLETION)
{
RpcRaiseException(APP_ERROR);
}
Nesse caso, o programa cliente entra em suspensão, não consumindo ciclos de CPU, até que a biblioteca de tempo de execução RPC chame o APC (não mostrado).
O exemplo a seguir demonstra um cliente que usa uma porta de conclusão de E/S para aguardar uma resposta assíncrona.
// 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);
}
No exemplo anterior, a chamada para GetQueuedCompletionStatus aguarda indefinidamente até que a chamada de procedimento remoto assíncrono seja concluída.
Uma possível armadilha ocorre ao escrever aplicativos multithread. Se um thread invocar uma chamada de procedimento remoto e terminar antes de receber a notificação de que o envio foi concluído, a chamada de procedimento remoto poderá falhar e o stub do cliente poderá fechar a conexão com o servidor. Portanto, os threads que chamam um procedimento remoto não devem ser encerrados antes que a chamada seja concluída ou cancelada quando o comportamento for indesejável.