Udostępnij za pośrednictwem


Oczekiwanie na asynchroniczną odpowiedź

To, co klient robi, gdy czeka na powiadomienie o odpowiedzi z serwera, zależy od wybranego mechanizmu powiadomień.

Jeśli klient używa zdarzenia do powiadamiania, zazwyczaj wywołuje funkcję WaitForSingleObject lub WaitForSingleObjectEx. Klient wprowadza zablokowany stan, gdy wywołuje jedną z tych funkcji. Jest to wydajne, ponieważ klient nie korzysta z cykli procesora, gdy jest zablokowany.

Gdy używa sondowania do oczekiwania na wyniki, program klienta wprowadza pętlę, która wielokrotnie wywołuje funkcję RpcAsyncGetCallStatus. Jest to efektywna metoda oczekiwania, jeśli program kliencki wykonuje inne przetwarzanie w pętli sondowania. Na przykład może przygotować dane w małych fragmentach do kolejnego asynchronicznego wywołania procedury zdalnej. Po zakończeniu każdego fragmentu Twój klient może sprawdzać trwające asynchroniczne wywołanie zdalnej procedury, aby zobaczyć, czy zostało ukończone.

Program kliencki może dostarczyć asynchroniczne wywołanie procedury (APC), które jest typem funkcji wywołania zwrotnego, którą biblioteka czasu wykonywania RPC wywoła po zakończeniu asynchronicznego wywołania procedury zdalnej. Program kliencki musi mieć stan oczekiwania z możliwością alertu. Zazwyczaj oznacza to, że klient wywołuje funkcję interfejsu API systemu Windows, aby umieścić się w stanie zablokowanym. Aby uzyskać więcej informacji, zobacz Asynchroniczne wywołania procedur.

Notatka

Powiadomienie o zakończeniu nie zostanie zwrócone z asynchronicznej procedury RPC, jeśli podczas wywołania asynchronicznego zostanie zgłoszony wyjątek RPC.

 

Jeśli program kliencki używa portu ukończenia we/wy do odbierania powiadomienia o ukończeniu, musi wywołać funkcję GetQueuedCompletionStatus. Gdy tak się stanie, może on czekać na czas nieokreślony na odpowiedź lub kontynuować inne przetwarzanie. Jeśli podczas oczekiwania na odpowiedź wykonywane jest inne przetwarzanie, musi odpytywać port ukończenia za pomocą funkcji GetQueuedCompletionStatus. W takim przypadku zazwyczaj należy ustawić dwMilliseconds na zero. Powoduje, że GetQueuedCompletionStatus zwraca wynik natychmiast, nawet jeśli wywołanie asynchroniczne nie zostało ukończone.

Programy klienckie mogą otrzymywać powiadomienia o zakończeniu za pośrednictwem kolejek komunikatów okien. W takiej sytuacji po prostu przetwarzają komunikat zakończenia tak, jak każdy komunikat systemu Windows.

W aplikacji wielowątkowej wywołanie asynchroniczne można anulować przez klienta dopiero po pomyślnym powrocie z wywołania przez wątek, który zainicjował wywołanie. Dzięki temu wywołanie nie zostanie asynchronicznie anulowane przez inny wątek, po niepowodzeniu synchronicznego wywołania. Zgodnie ze standardową praktyką, asynchroniczne wywołanie, które kończy się niepowodzeniem w sposób synchroniczny, nie powinno być anulowane w sposób asynchroniczny. Aplikacja kliencka musi uwzględniać to działanie, jeśli wywołania mogą być wykonywane i anulowane na różnych wątkach. Ponadto po anulowaniu wywołania kod klienta musi czekać na powiadomienie o zakończeniu i zakończyć wywołanie. Funkcja RpcAsyncCancelCall po prostu przyspiesza powiadomienie o zakończeniu; nie zastępuje to zakończenia połączenia.

Poniższy fragment kodu ilustruje, jak program kliencki może użyć zdarzenia do oczekiwania na asynchroniczną odpowiedź.

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

Programy klienckie, które używają APC do odbierania powiadomienia o asynchronicznej odpowiedzi, zwykle umieszczają się w stanie zablokowanym. Poniższy fragment kodu pokazuje to.

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

W takim przypadku program klienta przechodzi w stan uśpienia, nie zużywając cykli procesora CPU, dopóki biblioteka czasu wykonywania RPC nie wywołuje APC (nie pokazano).

W następnym przykładzie pokazano klienta, który używa portu zakończenia operacji we/wy do czekania na asynchroniczną odpowiedź.

// 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);
}

W poprzednim przykładzie wywołanie metody GetQueuedCompletionStatus czeka na czas nieokreślony aż do zakończenia asynchronicznego wywołania procedury zdalnej.

Podczas pisania aplikacji wielowątkowych występuje jedna potencjalna pułapka. Jeśli wątek wywołuje zdalne wywołanie procedury, a następnie kończy się przed odebraniem powiadomienia o zakończeniu wysyłania, zdalne wywołanie procedury może zakończyć się niepowodzeniem, a stub klienta może zamknąć połączenie z serwerem. W związku z tym wątki wywołujące procedurę zdalną nie powinny się kończyć przed ukończeniem lub anulowaniem wywołania, szczególnie gdy jest to niepożądane.