> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Modelo de programação assíncrona

> Modelo de programação assíncrona

O Microsoft Game Development Kit (GDK) implementa um novo padrão para APIs assíncronas que
atende aos comentários que recebemos de desenvolvedores de jogos em relação
ao padrão assíncrono implementado como parte do modelo de programação ERA do XBOX One.
Nosso objetivo é que esse novo padrão seja muito mais fácil de integrar a
arquiteturas de jogos típicas e ofereça aos desenvolvedores de jogos o alto grau de
controle que eles solicitaram. Este tópico descreve esse padrão de
design e oferece uma proposta de biblioteca que pode ser usada para
implementar padrões assíncronos.

<a id="gdk_asynchronous_model" />

## 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**

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/async_task_taskqueue_diagram.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=89181466bdb096dbacd570ff0f1dcac5" alt="Diagrama de uma tarefa e de uma fila de tarefas." width="660" height="314" data-path="images/gdk/features/common/async_task_taskqueue_diagram.png" />

Cada porta da fila de tarefas é configurada de forma diferente [no momento
da criação](/pt-BR/reference/system/xtaskqueue/functions/xtaskqueuecreate) para criar diferentes comportamentos de execução de retornos de chamada. Por
exemplo, a porta de trabalho pode ser configurada para ser assíncrona e a
porta de conclusão pode ser configurada para ser executada em série em um thread principal. Uma
configuração manual pode ser definida para permitir controle total sobre o comportamento
de execução. Os modos de configuração das portas são [explicados abaixo](#controlling_work_dispatching).

Quando uma tarefa assíncrona é iniciada, os retornos de chamada não são enfileirados na
fila de tarefas imediatamente. Um [provedor assíncrono](/pt-BR/build/core-features/common/async/async-libraries/async-library-xasyncprovider)
trata as alterações de estado para garantir que o trabalho seja enfileirado e despachado
antes que o retorno de chamada de conclusão seja enfileirado e despachado.

A fila de tarefas não trata diretamente o threading. Em vez disso, ela
depende de uma chamada externa para [despachar](/pt-BR/reference/system/xtaskqueue/functions/xtaskqueuedispatch) suas portas. As
chamadas externas determinam o comportamento de threading e simultaneidade. A
própria fila de tarefas é totalmente thread-safe.

**Figura 2.  Porta sendo despachada em vários threads**

<img src="https://mintcdn.com/microsoft-4404708b/sIrPFR_ir_sNKVEv/images/gdk/features/common/async_port_dispatch_multithread.png?fit=max&auto=format&n=sIrPFR_ir_sNKVEv&q=85&s=697aae48e97cb3aa23845108060a1882" alt="Imagem que mostra uma porta sendo despachada em vários threads." width="628" height="230" data-path="images/gdk/features/common/async_port_dispatch_multithread.png" />

Basicamente, é isso! Os retornos de chamada de uma tarefa são enfileirados nas portas
de trabalho e de conclusão de uma fila de tarefas, e essa fila de tarefas tem esses
retornos de chamada despachados de alguma maneira. A API contém um conjunto completo de
funcionalidades para gerenciar filas de tarefas, verificar o status de retornos de chamada, acompanhar
dados de trabalho, criar tratamento personalizado de tarefas e muito mais.

As chamadas de API assíncronas do Microsoft Game Development Kit (GDK) sempre implementam o retorno de chamada de trabalho internamente,
e os retornos de chamada de conclusão são sempre opcionais. Para usos além das chamadas
assíncronas do Microsoft Game Development Kit (GDK), você deve fornecer o retorno de chamada de trabalho.

## Requisitos

Os desenvolvedores de jogos listaram os seguintes requisitos para chamadas de API.

1. Preferir chamadas síncronas a chamadas assíncronas
2. Fornecer modo assíncrono com sondagem
3. Fornecer modo assíncrono com retornos de chamada
4. Fornecer controle sobre em qual thread o trabalho assíncrono é executado
5. 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](/pt-BR/reference/system/xasync/structs/xasyncblock):

```c++ theme={null}
typedef void CALLBACK XAsyncCompletionRoutine(struct XAsyncBlock* asyncBlock);

struct XAsyncBlock
{
    XTaskQueueHandle queue;
    void* context;
    XAsyncCompletionRoutine* callback;
    unsigned char internal[sizeof(void*) * 4];
};
```

Um [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock) é uma estrutura fornecida pelo chamador. O chamador preenche
os campos opcionais dessa estrutura, conforme mostrado na tabela a seguir.

| Campo | Descrição |
| - | - |
| **queue** | Um identificador de fila de tarefas que pode controlar quais threads executam a chamada assíncrona. Se esse parâmetro for nulo, a fila de tarefas do processo será usada. Se a fila de tarefas do processo tiver sido definida como nula, a chamada falhará com E\_NO\_TASK\_QUEUE. |
| **context** | Um ponteiro de contexto opcional que é passado para a função de retorno de chamada. |
| **callback** | Uma função de retorno de chamada opcional que é chamada quando a operação é concluída. |

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](/pt-BR/reference/system/xasync/structs/xasyncblock) deve permanecer na memória durante todo o
tempo de vida da operação assíncrona. Se o [XAsyncBlock](/pt-BR/reference/system/xasync/structs/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](/pt-BR/reference/system/xasync/structs/xasyncblock), há um pequeno número de APIs auxiliares, mostradas a seguir.

```c++ theme={null}
STDAPI XAsyncGetStatus(XAsyncBlock* asyncBlock, bool wait);

STDAPI XAsyncGetResultSize(XAsyncBlock* asyncBlock, size_t* bufferSize);

STDAPI_(void) XAsyncCancel(XAsyncBlock* asyncBlock);

typedef HRESULT CALLBACK XAsyncWork(XAsyncBlock* asyncBlock);

STDAPI XAsyncRun(XAsyncBlock* asyncBlock, XAsyncWork* work);
```

[XAsyncGetStatus](/pt-BR/reference/system/xasync/functions/xasyncgetstatus) retorna o status de uma chamada assíncrona. Quando a
chamada começa, esse status é E\_PENDING. Ele muda para S\_OK ou para um
erro específico quando concluída. Se a chamada for cancelada, ela retornará
E\_ABORT.

[XAsyncGetResultSize](/pt-BR/reference/system/xasync/functions/xasyncgetresultsize) retorna o tamanho de buffer necessário para obter os
resultados da chamada. A API real para buscar os resultados é adaptada a
cada chamada assíncrona.

[XAsyncCancel](/pt-BR/reference/system/xasync/functions/xasynccancel) pode ser usada para cancelar uma chamada. O cancelamento depende da operação que está sendo cancelada e pode ocorrer de forma síncrona, assíncrona ou nem ocorrer. Se uma operação for cancelada,
[XAsyncGetResult](/pt-BR/reference/system/xasyncprovider/functions/xasyncgetresult), [XAsyncGetResultSize](/pt-BR/reference/system/xasync/functions/xasyncgetresultsize) ou [XAsyncGetStatus](/pt-BR/reference/system/xasync/functions/xasyncgetstatus) retornarão E\_ABORT. Uma chamada
cancelada sinaliza o parâmetro [XAsyncCompletionRoutine](/pt-BR/reference/system/xasync/functions/xasynccompletionroutine) do [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock) e invoca seu
retorno de chamada.

[XAsyncRun](/pt-BR/reference/system/xasync/functions/xasyncrun) é um método auxiliar que pode executar qualquer código de forma assíncrona.

### Uso da API assíncrona

Primeiro, vamos analisar uma API síncrona no exemplo de código a seguir.

```c++ theme={null}
HRESULT XGameSaveGetRemainingQuota(XGameSaveProviderHandle provider,
int64_t* remainingQuota);
```

Essa API chama um serviço web para determinar quanto armazenamento de jogos salvos
ainda resta. Para adicionar suporte assíncrono, declaramos um par de novas
APIs.

```c++ theme={null}
HRESULT XGameSaveGetRemainingQuotaAsync(XGameSaveProviderHandle
provider, XAsyncBlock* async);

HRESULT XGameSaveGetRemainingQuotaResult(XAsyncBlock* async,
int64_t* remainingQuota);
```

[XGameSaveGetRemainingQuotaAsync](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) retorna S\_OK se a chamada assíncrona tiver sido
iniciada (como essa API é somente assíncrona, não há valor em retornar
E\_PENDING). [XGameSaveGetRemainingQuotaResult](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) retorna E\_PENDING
até que a chamada seja concluída.

Vamos ver isso na prática, conforme mostrado a seguir.

```c++ theme={null}
// providerHandle is a previously obtained XGameSaveProviderHandle.

XAsyncBlock* b = new XAsyncBlock;
ZeroMemory(b, sizeof(XAsyncBlock));
b->context = this;
b->queue = queue;
b->callback = [](XAsyncBlock* async)
{
    int64_t remainingQuota;
    if(SUCCEEDED(XGameSaveGetRemainingQuotaResult(async, &remainingQuota)))
    {
        printf("Remaining quota: %irn", remainingQuota);
    }
    delete async;
};
XGameSaveGetRemainingQuotaAsync(providerHandle, b);
```

Todos os [XAsyncBlocks](/pt-BR/reference/system/xasync/structs/xasyncblock) exigem uma fila de tarefas (descrita a seguir), que controla
onde e como a chamada assíncrona é executada. Uma fila de tarefas de todo o processo
é usada se nenhuma for fornecida.

Observe que o [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock) precisa permanecer na memória durante toda a vida da
chamada assíncrona. Neste exemplo, ele foi alocado dinamicamente e excluído no
retorno de chamada de conclusão. Ele também poderia ser armazenado como uma variável global ou
membro. Ocorre um comportamento indefinido se o mesmo [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock) for usado para
mais de uma chamada assíncrona ao mesmo tempo.

[XGameSaveGetRemainingQuotaResult](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) conclui o ciclo de uma chamada assíncrona.
Ela libera os dados internos do bloco assíncrono, de modo que o bloco agora pode ser
usado para uma nova chamada. Chamadas subsequentes a
[XGameSaveGetRemainingQuotaResult](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) falham.
[XGameSaveGetRemainingQuotaAsync](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) e [XGameSaveGetRemainingQuotaResult](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) também
são pareadas dentro do bloco assíncrono: ocorrerá um erro se você
combinar uma chamada assíncrona com outra API de resultado incompatível.

Se uma chamada assíncrona não tiver carga de dados, ou seja, se apenas o status HRESULT for
importante, defina um método **Result** que receba apenas o bloco assíncrono, conforme mostrado a seguir.

```c++ theme={null}
HRESULT QueryUpdateStatusAsyncResult(_Inout_ XAsyncBlock* block);
```

<a id="controlling_work_dispatching" />

### 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](/pt-BR/reference/system/xasync/structs/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.

Há uma fila de tarefas de processo padrão configurada para que tanto as portas de trabalho
quanto as portas de conclusão sejam despachadas pelo pool de threads do sistema. Essa
fila de tarefas do processo é usada se nenhum parâmetro de fila for passado no
[XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock). Um jogo também pode desabilitar a fila de tarefas do processo, exigindo que uma
fila seja passada no [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock).

Esperamos que muitos desenvolvedores escolham o modo de despacho manual para
exercer controle total sobre quando e onde o trabalho assíncrono e os retornos de chamada
de conclusão são executados.

Para obter detalhes sobre filas de tarefas, consulte [Design da fila de tarefas assíncronas](/pt-BR/build/core-features/common/async/async-task-queue-design).

## 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.

1. Modo assíncrono com sondagem
2. Modo assíncrono com retornos de chamada
3. Controle sobre em qual thread os retornos de chamada ocorrem

As notificações usam uma fila de tarefas para permitir que o desenvolvedor controle
o thread do retorno de chamada, mas, fora isso, não usam blocos assíncronos: elas são
projetadas para se parecer mais com eventos padrão, com métodos **Register** e **Unregister**.

* 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.

Vamos analisar o exemplo a seguir, que poderia buscar mensagens do Windows.

```c++ theme={null}
struct XTaskQueueRegistrationToken;

typedef void MessageAvailableCallback(void* context, const MSG* msg);

HRESULT RegisterMessageAvailable(
    XTaskQueueHandle queue,
    void* context,
    MessageAvailableCallback* callback,
    XTaskQueueRegistrationToken * token);

bool UnregisterMessageAvailable(XTaskQueueRegistrationToken token, bool
wait);

// Usage.
XTaskQueueRegistrationToken token;
RegisterMessageAvailable(queue, nullptr, [](void*, const MSG* msg)
{
    printf("Message: %drn", msg->message);
}, &token);
```

Observe que, neste exemplo, **UnregisterMessageAvailable** recebe um parâmetro final
"wait" e retorna um bool. Isso permite que os chamadores decidam como
tratar o cancelamento do registro enquanto uma chamada está sendo invocada.

<a id="heading-7" />

## 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.

```c++ theme={null}
enum class XAsyncOp : uint32_t
{
    Begin,
    DoWork,
    GetResult,
    Cancel,
    Cleanup
};

struct XAsyncProviderData
{
    XAsyncBlock* async;  
    size_t bufferSize;  
    void* buffer;  
    void* context;
};

typedef HRESULT CALLBACK XAsyncProvider(
_In_ XAsyncOp op,
_Inout_ XAsyncProviderData* data);

STDAPI XAsyncBegin (
_Inout_ XAsyncBlock* asyncBlock,
_In_opt_ void* context,
_In_opt_ void* identity,
_In_opt_ const char* identityName,
_In_ XAsyncProvider* provider);

STDAPI XAsyncSchedule(
_Inout_ XAsyncBlock* asyncBlock,
_In_ uint32_t delayInMs);

STDAPI_(void) XAsyncComplete(
_Inout_ XAsyncBlock* asyncBlock,
_In_ HRESULT result,
_In_ size_t requiredBufferSize);

STDAPI XAsyncGetResult(
_Inout_ XAsyncBlock* asyncBlock,
_In_opt_ void* identity,
_In_ size_t bufferSize,
_Out_writes_bytes_opt_(bufferSize) void* buffer,
_Out_opt_ size_t* bufferUsed);
```

Essa API usa um único retorno de chamada, combinado com um valor de operação que
indica por que a API está sendo chamada. Há também uma única estrutura de
dados que é preenchida à medida que a chamada avança. Para usar essa API,
faça o seguinte.

1. Chame [XAsyncBegin](/pt-BR/reference/system/xasyncprovider/functions/xasyncbegin) com o bloco assíncrono passado pelo chamador e
   forneça um retorno de chamada que forneça a implementação.

2. Execute o trabalho assíncrono da chamada. Se você precisar executar o trabalho em
   um thread de trabalho, chame [XAsyncSchedule](/pt-BR/reference/system/xasyncprovider/functions/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.

3. 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](/pt-BR/reference/system/xasyncprovider/functions/xasyncschedule) de dentro de um worker para reagendar trabalho
   adicional.

4. Quando todo o trabalho estiver concluído, chame [XAsyncComplete](/pt-BR/reference/system/xasyncprovider/functions/xasynccomplete).

5. Forneça um wrapper fortemente tipado em torno de [XAsyncGetResult](/pt-BR/reference/system/xasyncprovider/functions/xasyncgetresult) para retornar
   os resultados.

6. Se a sua chamada assíncrona não tiver carga de dados, você deverá fornecer um wrapper
   fortemente tipado em torno de [XAsyncGetStatus](/pt-BR/reference/system/xasync/functions/xasyncgetstatus) e passar zero como o tamanho de
   buffer necessário para [XAsyncComplete](/pt-BR/reference/system/xasyncprovider/functions/xasynccomplete).

O retorno de chamada do provedor assíncrono é invocado com as seguintes
operações.

* **Begin** Um provedor assíncrono é invocado com esse opcode durante [XAsyncBegin](/pt-BR/reference/system/xasyncprovider/functions/xasyncbegin). Se o provedor implementar esse opcode, ele deverá iniciar sua tarefa assíncrona chamando [XAsyncSchedule](/pt-BR/reference/system/xasyncprovider/functions/xasyncschedule) ou por meios externos. Esse retorno de chamada é chamado de forma síncrona na cadeia de chamadas de [XAsyncBegin](/pt-BR/reference/system/xasyncprovider/functions/xasyncbegin), portanto, nunca deve bloquear.

* **DoWork** Chamado nos casos em que [XAsyncSchedule](/pt-BR/reference/system/xasyncprovider/functions/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](/pt-BR/reference/system/xasyncprovider/functions/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](/pt-BR/reference/system/xasyncprovider/functions/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](/pt-BR/reference/system/xasyncprovider/functions/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.

Um provedor assíncrono só precisa implementar as operações de que necessita. Por
exemplo, uma E/S assíncrona não cancelável que não tem limpeza só precisa
implementar **GetResult**.

Veja a seguir um exemplo de um método **FactorialAsync** que implementa o fatorial
de forma assíncrona.

```c++ theme={null}
UINT64 Factorial(UINT64 value)
{
    UINT64 result = 1;

    while (value != 0)
    {       
        result *= value;
        value--;
    }

    return result;
}

HRESULT FactorialAsync(UINT64 value, XAsyncBlock* async)
{
    struct CallData
    {
        UINT64 value;
        UINT64 result;
    };

    CallData* data = new CallData();
    data->value = value;
    data->result = 1;

    HRESULT hr = XAsyncBegin (async, data, FactorialAsync, __FUNCTION__, []
        (XAsyncOp op, XAsyncProviderData* data)
    {
        CallData* d = (CallData*)data->context;

        switch (op)
        {
        case XAsyncOp::Begin:
            return XAsyncSchedule(data->async, 0);

        case XAsyncOp::Cleanup:
            delete d;
            break;

        case XAsyncOp::GetResult:
            CopyMemory(data->buffer, &d->result, sizeof(UINT64));
            break;
 
        case XAsyncOp::DoWork:
            data->result = Factorial(data.Value);
            XAsyncComplete(data->async, S_OK, sizeof(UINT64));
            break;
        }

        return S_OK;
    });

    return hr;
}

HRESULT FactorialAsyncResult(XAsyncBlock* async, UINT64* result)
{
    return XAsyncGetResult(async, FactorialAsync, sizeof(UINT64), result);
}
```

## Documentação de referência da API

* [XAsync (conteúdo da API)](/pt-BR/reference/system/xasync/xasync_members)
  * Funções
    * [XAsyncGetStatus](/pt-BR/reference/system/xasync/functions/xasyncgetstatus)
    * [XAsyncGetResultSize](/pt-BR/reference/system/xasync/functions/xasyncgetresultsize)
    * [XAsyncCancel](/pt-BR/reference/system/xasync/functions/xasynccancel)
    * [XAsyncCompletionRoutine](/pt-BR/reference/system/xasync/functions/xasynccompletionroutine)
    * [XAsyncRun](/pt-BR/reference/system/xasync/functions/xasyncrun)
  * Estruturas
    * [XAsyncBlock](/pt-BR/reference/system/xasync/structs/xasyncblock)
* [XAsyncProvider (conteúdo da API)](/pt-BR/reference/system/xasyncprovider/xasyncprovider_members)
  * Funções
    * [XAsyncGetResult](/pt-BR/reference/system/xasyncprovider/functions/xasyncgetresult)
    * [XAsyncBegin](/pt-BR/reference/system/xasyncprovider/functions/xasyncbegin)
    * [XAsyncSchedule](/pt-BR/reference/system/xasyncprovider/functions/xasyncschedule)
    * [XAsyncComplete](/pt-BR/reference/system/xasyncprovider/functions/xasynccomplete)
* [XTaskQueue (conteúdo da API)](/pt-BR/reference/system/xtaskqueue/xtaskqueue_members)
  * Funções
    * [xtaskqueuecreate](/pt-BR/reference/system/xtaskqueue/functions/xtaskqueuecreate)
    * [xtaskqueuedispatch](/pt-BR/reference/system/xtaskqueue/functions/xtaskqueuedispatch)
* [xgamesave (conteúdo da API)](/pt-BR/reference/system/xgamesave/xgamesave_members)
  * Funções
    * [XGameSaveGetRemainingQuotaAsync](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync)
    * [XGameSaveGetRemainingQuotaResult](/pt-BR/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult)

## Consulte também

[Metas de design e melhorias da programação assíncrona](/pt-BR/build/core-features/common/async/async-whitepaper)
[Design da fila de tarefas assíncronas](/pt-BR/build/core-features/common/async/async-task-queue-design)


## Related topics

- [Guia de programação do Direct3D 12](/pt-BR/build/gdk-and-engines/guides/directx-12-programming-guide.md)
- [Documentação de desenvolvimento de jogos XBOX](/pt-BR/index.md)
- [Advanced Shader Delivery](/pt-BR/build/gdk-and-engines/guides/advanced-shader-delivery.md)
- [Referência da API C dos Serviços PlayFab](/pt-BR/services/playfab/api-references/c/index.md)
- [Modelos de precios](/es/publishing/game-publishing/concepts/availability/managed-creators/reseller-pricing.md)
