Chamadas de procedimento assíncrono

Uma APC ( chamada de procedimento assíncrono ) é uma função que é executada de forma assíncrona no contexto de um thread específico. Quando um APC é enfileirado em um thread, o sistema emite uma interrupção de software. Na próxima vez que o thread for agendado, ele executará a função APC. Um APC gerado pelo sistema é chamado de APC no modo kernel. Um APC gerado por um aplicativo é chamado de APC no modo de usuário. Um thread deve estar em um estado alertável para executar um APC no modo de usuário.

Cada thread tem sua própria fila de APC. Um aplicativo enfileira um APC para um thread chamando a função QueueUserAPC . O thread de chamada especifica o endereço de uma função APC na chamada para QueueUserAPC. O enfileiramento de um APC é uma solicitação para o thread chamar a função APC.

Quando um APC no modo de usuário é enfileirado, o thread para o qual ele está enfileirado não é direcionado para chamar a função APC, a menos que esteja em um estado alertável. Um thread entra em um estado alertável quando chama a função SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx ou WaitForSingleObjectEx . Se a espera for atendida antes que o APC seja enfileirado, o thread não estará mais em um estado de espera alertável para que a função APC não seja executada. No entanto, o APC ainda está na fila, portanto, a função APC será executada quando o thread chamar outra função de espera alertável.

As funções ReadFileEx, SetWaitableTimer, SetWaitableTimerEx e WriteFileEx são implementadas usando um APC como o mecanismo de retorno de chamada de notificação de conclusão.

Se você estiver usando um pool de threads, observe que as APCs não funcionam tão bem quanto outros mecanismos de sinalização porque o sistema controla o tempo de vida dos threads do pool de threads, portanto, é possível que um thread seja encerrado antes que a notificação seja entregue. Em vez de usar um mecanismo de sinalização baseado em APC, como o parâmetro pfnCompletionRoutine de SetWaitableTimer ou SetWaitableTimerEx, use um objeto de espera, como um temporizador criado com CreateThreadpoolTimer. Para E/S, use um objeto de conclusão de E/S criado com CreateThreadpoolIo ou uma estrutura OVERLAPPED baseada em hEvent em que o evento pode ser passado para a função SetThreadpoolWait.

Sincronização interna

Quando uma solicitação de E/S é emitida, uma estrutura é alocada para representar a solicitação. Essa estrutura é chamada de IRP (pacote de solicitação de E/S). Com e/S síncrona, o thread cria o IRP, envia-o para a pilha do dispositivo e aguarda no kernel para que o IRP seja concluído. Com e/S assíncrona, o thread cria o IRP e o envia para a pilha do dispositivo. A pilha pode concluir o IRP imediatamente ou pode retornar uma status pendente indicando que a solicitação está em andamento. Quando isso acontece, o IRP ainda está associado ao thread, portanto, ele será cancelado se o thread terminar ou chamar uma função como CancelIo. Enquanto isso, o thread pode continuar a executar outras tarefas enquanto a pilha de dispositivos continua processando o IRP.

Há várias maneiras pelas quais o sistema pode indicar que o IRP foi concluído:

  • Atualize a estrutura sobreposta com o resultado da operação para que o thread possa sondar para determinar se a operação foi concluída.
  • Sinalize o evento na estrutura sobreposta para que um thread possa sincronizar no evento e ser acordado quando a operação for concluída.
  • Coloque o IRP na APC pendente do thread para que o thread execute a rotina APC quando entrar em um estado de espera alertável e retornar da operação de espera com um status indicando que ele executou uma ou mais rotinas de APC.
  • Enfileirar o IRP para uma porta de conclusão de E/S, em que ele será executado pelo próximo thread que aguarda a porta de conclusão.

Os threads que esperam em uma porta de conclusão de E/S não esperam em um estado alertável. Portanto, se esses threads emitirem IRPs definidos para serem concluídos como APCs para o thread, essas conclusões de IPC não ocorrerão em tempo hábil; elas ocorrerão somente se o thread pegar uma solicitação da porta de conclusão de E/S e, em seguida, inserir uma espera alertável.

Usando um temporizador de espera com uma chamada de procedimento assíncrono