Modelo conceitual
A programação assíncrona no Microsoft Game Development Kit (GDK) é dividida em 2 componentes principais: tarefas e filas de tarefas. Embora haja mais funcionalidades nas bibliotecas, todo o modelo conceitual utiliza esses 2 componentes principais. Uma tarefa é um conjunto único de trabalho assíncrono que pode ser iniciado, ter seu status verificado, possivelmente ser cancelado, ser concluído e retornar suas informações de conclusão. No modelo do Microsoft Game Development Kit (GDK), as tarefas são compostas de dois corpos: o retorno de chamada de trabalho e o retorno de chamada de conclusão. Isso permite mais controle, como processamento totalmente paralelo ou trabalho paralelo combinado com conclusão em thread único. Uma fila de tarefas é um contêiner que enfileira retornos de chamada de trabalho e de conclusão para execução posterior. Há duas filas internas em uma fila de tarefas, chamadas portas, que tratam os retornos de chamada de trabalho e de conclusão separadamente. Elas são chamadas de porta de trabalho e porta de conclusão. Figura 1. Diagrama de uma tarefa e de uma fila de tarefas

Requisitos
Os desenvolvedores de jogos listaram os seguintes requisitos para chamadas de API.- Preferir chamadas síncronas a chamadas assíncronas
- Fornecer modo assíncrono com sondagem
- Fornecer modo assíncrono com retornos de chamada
- Fornecer controle sobre em qual thread o trabalho assíncrono é executado
- Fornecer controle sobre em qual thread os retornos de chamada de conclusão são executados
Tipos de APIs
O Microsoft Game Development Kit (GDK) se esforça para ser muito simples no design de suas APIs. Os desenvolvedores de jogos são especialistas em ajustar seu código para maximizar o uso do hardware. Damos controle a eles sempre que possível. As implementações de API se dividem nos tipos a seguir.- Seguras para tempo crítico: Uma API segura para tempo crítico é aquela que pode ser chamada em um thread sensível ao tempo. Observe que, embora isso geralmente signifique que a API é trivial ou muito rápida, o conceito-chave é que as características de desempenho da API são consistentes. Elas são sempre síncronas e nunca precisam ter uma versão assíncrona. Essas APIs devem ser documentadas como seguras para tempo crítico.
- Não seguras para tempo crítico: Não é seguro chamar essas APIs a partir do thread de renderização. Suas características de desempenho podem variar amplamente. A maioria das APIs se enquadra nessa categoria.
- Assíncronas: Essas APIs são assíncronas por natureza, como uma chamada de serviço web. Elas usam o padrão assíncrono descrito neste tópico. As APIs assíncronas não são tão comuns no Microsoft Game Development Kit (GDK) quanto no modelo de programação ERA do XBOX One: uma API assíncrona geralmente é de longa duração e cancelável. Exceto em alguns casos de uso específicos, as APIs assíncronas terão uma versão síncrona não segura para tempo crítico. Chamar uma API assíncrona deve sempre ser seguro para tempo crítico.
- Notificações: As notificações são periódicas por natureza e não têm um fim definido. Elas estão relacionadas às APIs assíncronas, mas, devido à sua natureza periódica, devem ter aparência e comportamento diferentes para os desenvolvedores. O registro para uma notificação deve sempre ser seguro para tempo crítico.
Padrão de API assíncrona
O Microsoft Game Development Kit (GDK) introduz um padrão de API assíncrona de uso geral que os componentes do Microsoft Game Development Kit (GDK) podem usar para fornecer suporte assíncrono consistente. No centro dele está uma estrutura semelhante a OVERLAPPED chamada XAsyncBlock:
Os campos Internal são usados pelo sistema e não devem ser modificados.
Os campos configuráveis pelo usuário nessa estrutura não devem ser modificados
durante uma operação assíncrona. Um XAsyncBlock deve permanecer na memória durante todo o
tempo de vida da operação assíncrona. Se o XAsyncBlock for alocado dinamicamente,
o retorno de chamada de conclusão é o momento mais cedo em que ele pode ser excluído.
Além do XAsyncBlock, há um pequeno número de APIs auxiliares, mostradas a seguir.
Uso da API assíncrona
Primeiro, vamos analisar uma API síncrona no exemplo de código a seguir.Controlando o despacho de trabalho
Qual thread executou o trabalho assíncrono nas chamadas anteriores? Qual thread invocou o retorno de chamada de conclusão? Isso é decidido pela fila de tarefas atribuída ao XAsyncBlock. As filas de tarefas têm duas “portas”: uma porta de trabalho e uma porta de conclusão. Cada porta tem um modo de despacho que determina como os retornos de chamada enfileirados em uma porta são processados. Há vários modos de despacho.- Pool de threads: Os retornos de chamada enfileirados em uma fila de pool de threads são executados no pool de threads do sistema. O pool de threads invoca as chamadas em paralelo, retirando uma chamada da fila para executar, uma de cada vez, à medida que os threads do pool ficam disponíveis.
- Pool de threads serializado: Os retornos de chamada são enfileirados e executados no pool de threads, mas um de cada vez.
- Manual: Os retornos de chamada enfileirados em uma fila manual não são despachados automaticamente. Cabe ao desenvolvedor despachá-los em qualquer thread que desejar.
- Imediato: O modo de despacho imediato não usa fila. Ele executa imediatamente a chamada no thread que enviou o retorno de chamada.
Notificações
Uma notificação pode não ter fim e pode ser chamada muitas vezes. As notificações devem oferecer suporte a um subconjunto dos requisitos de uma chamada assíncrona.- Modo assíncrono com sondagem
- Modo assíncrono com retornos de chamada
- Controle sobre em qual thread os retornos de chamada ocorrem
- Um método Register que recebe quaisquer parâmetros específicos da chamada, uma fila de tarefas, um contexto void opcional e um ponteiro de retorno de chamada fortemente tipado. O último parâmetro é um parâmetro out que retorna um token.
- Um método Unregister que recebe qualquer contexto específico da chamada e o token.
- A sondagem é suportada pela adição de um método separado que não está relacionado ao retorno de chamada de notificação.
Biblioteca assíncrona
Para facilitar a criação de APIs consistentes que ofereçam suporte ao padrão assíncrono, fornecemos uma biblioteca que pode ser usada para implementar a “infraestrutura assíncrona” de uma API. A API da biblioteca tem a seguinte aparência.- Chame XAsyncBegin com o bloco assíncrono passado pelo chamador e forneça um retorno de chamada que forneça a implementação.
- Execute o trabalho assíncrono da chamada. Se você precisar executar o trabalho em um thread de trabalho, chame XAsyncSchedule. Se você puder realizar o trabalho usando primitivos assíncronos do sistema operacional e configurar esses primitivos rápido o suficiente para continuar seguro para tempo crítico, essa é a opção preferencial.
- Se você precisar invocar outro trabalho assíncrono a partir de um retorno de chamada de thread de trabalho, poderá retornar E_PENDING do worker. Você também pode chamar XAsyncSchedule de dentro de um worker para reagendar trabalho adicional.
- Quando todo o trabalho estiver concluído, chame XAsyncComplete.
- Forneça um wrapper fortemente tipado em torno de XAsyncGetResult para retornar os resultados.
- Se a sua chamada assíncrona não tiver carga de dados, você deverá fornecer um wrapper fortemente tipado em torno de XAsyncGetStatus e passar zero como o tamanho de buffer necessário para XAsyncComplete.
- Begin Um provedor assíncrono é invocado com esse opcode durante XAsyncBegin. Se o provedor implementar esse opcode, ele deverá iniciar sua tarefa assíncrona chamando XAsyncSchedule ou por meios externos. Esse retorno de chamada é chamado de forma síncrona na cadeia de chamadas de XAsyncBegin, portanto, nunca deve bloquear.
- DoWork Chamado nos casos em que XAsyncSchedule foi chamado para agendar trabalho assíncrono usando a fila de tarefas. A função do provedor faz todo o trabalho necessário. Ao concluir, ela chama XAsyncComplete com o código de resultado e o tamanho da carga de dados, que pode ser zero se não houver carga de dados da chamada. Se for necessário realizar mais trabalho assíncrono, o provedor poderá agendar esse trabalho e deverá retornar E_PENDING.
- GetResult Chamado para buscar o resultado da chamada. Como o tamanho dos dados é passado para XAsyncComplete durante a conclusão da chamada, nenhuma verificação de argumentos é necessária aqui: todos os buffers e tamanhos de buffer foram verificados pela biblioteca.
- Cancel Chamado quando o usuário cancela uma chamada assíncrona. Se a chamada puder ser cancelada, cancele-a e chame XAsyncComplete com E_ABORT como código de resultado.
- Cleanup Chamado quando a chamada foi totalmente concluída e o provedor pode excluir qualquer memória dinâmica.
Documentação de referência da API
- XAsync (conteúdo da API)
- Funções
- Estruturas
- XAsyncProvider (conteúdo da API)
- XTaskQueue (conteúdo da API)
- Funções
- xgamesave (conteúdo da API)
