Aguardando a resposta assíncrona
O que o cliente faz enquanto espera 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çãoWaitForSingleObject ou a funçãoWaitForSingleObjectEx. O cliente entra em um estado bloqueado quando chama qualquer uma dessas funções. Isso é eficiente porque o cliente não consome ciclos de execução da CPU enquanto está bloqueado.
Quando ele usa sondagem para aguardar seus resultados, o programa cliente entra em um loop que chama repetidamente a função RpcAsyncGetCallStatus. Este é um método eficiente de espera se o seu programa cliente fizer outro processamento no loop de sondagem. Por exemplo, ele pode preparar dados em pequenos blocos para uma chamada de procedimento remoto assíncrona subsequente. Depois que cada segmento for concluído, o seu cliente poderá consultar a chamada de procedimento remoto assíncrono pendente para verificar se foi concluída.
O seu programa cliente pode fornecer uma chamada de procedimento assíncrono (APC), que é um tipo de função de callback que a biblioteca de execução em tempo real do RPC irá invocar quando a chamada de procedimento remoto assíncrono for concluída. O seu programa cliente deve encontrar-se num estado de espera alertável. Isso normalmente significa que o cliente chama uma função de 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 a notificação de conclusão, ele deverá chamar a função GetQueuedCompletionStatus. Quando o faz, pode esperar indefinidamente por uma resposta ou continuar a fazer outro processamento. Se ele realizar outro processamento enquanto aguarda uma resposta, ele deve sondar a porta de conclusão com a função GetQueuedCompletionStatus. Nesse caso, ele normalmente precisa definir o 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ções de conclusão através 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 pode ser cancelada pelo cliente somente 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 falhar 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 podem ser emitidas e canceladas em threads diferentes. Além disso, depois que a chamada for cancelada, o código do cliente deve 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 substitui o preenchimento da 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 notificação de uma resposta assíncrona normalmente colocam-se 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 próximo exemplo 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 armadilha potencial ocorre ao escrever aplicativos multithreaded. Se um thread invocar uma chamada de procedimento remoto e, em seguida, termina antes de receber a notificação de que o envio foi concluído, a chamada de procedimento remoto pode falhar e o stub do cliente pode fechar a conexão com o servidor. Portanto, as threads que chamam um procedimento remoto não devem ser encerradas antes que a chamada seja concluída ou, em casos de comportamento indesejável, cancelada.