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

# Fazendo chamadas assíncronas na API C da XSAPI

> Use XAsyncBlock e XTaskQueueHandle para controlar as threads das chamadas assíncronas da XSAPI C, como XblProfileGetUserProfileAsync, em títulos do XBOX Live.

Uma **API assíncrona** é uma API que retorna rapidamente, mas inicia uma **tarefa assíncrona**, e o resultado é retornado depois que a tarefa é concluída.

Tradicionalmente, os jogos têm pouco controle sobre qual thread executa a **tarefa assíncrona** e qual thread retorna os resultados ao usar um **callback de conclusão**.
Alguns jogos são projetados para que uma seção do heap seja acessada por apenas uma thread, a fim de evitar qualquer necessidade de sincronização de threads.
Se o **callback de conclusão** não for chamado a partir de uma thread controlada pelo jogo, atualizar o estado compartilhado com o resultado de uma **tarefa assíncrona** exigirá sincronização de threads.

A API C da XSAPI expõe uma nova API C assíncrona que dá aos desenvolvedores controle direto das threads ao
fazer uma chamada a uma **API assíncrona**, como **XblSocialGetSocialRelationshipsAsync()**, **XblProfileGetUserProfileAsync()** e **XblAchievementsGetAchievementsForTitleIdAsync()**.

Aqui está um exemplo básico que chama a API **XblProfileGetUserProfileAsync**:

```cpp theme={null}
 XAsyncBlock* asyncBlock = new XAsyncBlock();
    asyncBlock->queue = GlobalState()->queue;
    asyncBlock->context = nullptr;
    asyncBlock->callback = [](XAsyncBlock* asyncBlock)
    {
        XblUserProfile profile = { 0 };
        HRESULT hr = XblProfileGetUserProfileResult(asyncBlock, &profile);
        delete asyncBlock;
    };

    HRESULT hr = XblProfileGetUserProfileAsync(GlobalState()->xboxLiveContext, GlobalState()->xboxUserId, asyncBlock);
```

Para entender esse padrão de chamada, você precisará entender como usar o **XAsyncBlock** e o **XTaskQueueHandle**.

* O **XAsyncBlock** carrega todas as informações relacionadas à **tarefa assíncrona** e ao **callback de conclusão**.

* O **XTaskQueueHandle** permite determinar qual thread executa a **tarefa assíncrona** e qual thread chama o **callback de conclusão** do XAsyncBlock.

## O **XAsyncBlock**

Vamos examinar o **XAsyncBlock** em detalhes.
Ele é uma struct definida da seguinte forma:

```cpp theme={null}
typedef struct XAsyncBlock
{
    /// <summary>
    /// The queue to queue the call on
    /// </summary>
    XTaskQueueHandle queue;

    /// <summary>
    /// Optional context pointer to pass to the callback
    /// </summary>
    void* context;

    /// <summary>
    /// Optional callback that will be invoked when the call completes
    /// </summary>
    XAsyncCompletionRoutine* callback;

    /// <summary>
    /// Internal use only
    /// </summary>
    unsigned char internal[sizeof(void*) * 4];
};
```

O **XAsyncBlock** contém:

* *queue* - um XTaskQueueHandle, que é um identificador que representa informações sobre onde executar uma parte do trabalho. Se não for definido, uma fila padrão será usada.

* *context* - permite passar dados para a função de callback.

* *callback* - uma função de callback opcional que será chamada depois que o trabalho assíncrono for concluído. Se você não especificar um callback, poderá aguardar a conclusão do **XAsyncBlock** com **XAsyncGetStatus** e, em seguida, obter os resultados.

Você deve criar um novo XAsyncBlock no heap para cada API assíncrona que chamar.
O XAsyncBlock precisa existir até que o callback de conclusão do XAsyncBlock seja chamado; depois disso, ele pode ser excluído.

> **Importante:**
> Um **XAsyncBlock** precisa permanecer na memória até que a **tarefa assíncrona** seja concluída. Se ele for alocado dinamicamente, poderá ser excluído dentro do **callback de conclusão** do XAsyncBlock.

### Aguardando uma **tarefa assíncrona**

Você pode saber que uma **tarefa assíncrona** foi concluída de várias maneiras diferentes:

* O **callback de conclusão** do XAsyncBlock é chamado.
* Chame **XAsyncGetStatus** com true para aguardar até que ela seja concluída.

Com **XAsyncGetStatus**, a **tarefa assíncrona** é considerada concluída depois que o **callback de conclusão** do XAsyncBlock é executado; no entanto, o **callback de conclusão** do XAsyncBlock é opcional.

Depois que a **tarefa assíncrona** for concluída, você poderá obter os resultados.

### Obtendo o resultado da **tarefa assíncrona**

Para obter o resultado, a maioria das funções de **API assíncrona** tem uma função \[Nome da função]Result correspondente para receber o resultado da chamada assíncrona.

No nosso código de exemplo, **XblProfileGetUserProfileAsync** tem uma função **XblProfileGetUserProfileResult** correspondente.
Você pode usar essa função para recuperar o resultado da função e agir de acordo.

Para obter detalhes completos sobre como recuperar resultados, consulte a documentação de cada função de **API assíncrona**.

## O **XTaskQueueHandle**

O **XTaskQueueHandle** permite determinar qual thread executa a **tarefa assíncrona** e qual thread chama o **callback de conclusão** do XAsyncBlock.

Você pode controlar qual thread executa essas operações definindo um *modo de despacho*.
Há três modos de despacho disponíveis:

* *Manual* - As filas manuais não são despachadas automaticamente. Cabe ao desenvolvedor despachá-las em qualquer thread que desejar. Isso pode ser usado para atribuir o lado de trabalho ou o lado de callback de uma chamada assíncrona a uma thread específica. Isso é discutido em mais detalhes abaixo.

* *Thread Pool* - Despacha usando um pool de threads. O pool de threads invoca as chamadas em paralelo, retirando da fila uma chamada para executar à medida que as threads do pool ficam disponíveis. É o mais fácil de usar, mas oferece o menor controle sobre qual thread é usada.

* *Serialized Thread Pool* - Despacha usando um pool de threads. O pool de threads invoca as chamadas em série, retirando da fila uma chamada para executar à medida que a única thread do pool fica disponível.

* *Immediate* - Despacha imediatamente o trabalho enfileirado na thread a partir da qual ele foi enviado.

Para criar um novo **XTaskQueueHandle**, você precisará chamar **XTaskQueueCreate**.
Por exemplo:

```cpp theme={null}
STDAPI XTaskQueueCreate(
    _In_ XTaskQueueDispatchMode workDispatchMode,
    _In_ XTaskQueueDispatchMode completionDispatchMode,
    _Out_ XTaskQueueHandle* queue
    ) noexcept;
```

Essa função recebe dois parâmetros `XTaskQueueDispatchMode`.
Há três valores possíveis para `XTaskQueueDispatchMode`:

```cpp theme={null}
/// <summary>
/// Describes how task queue callbacks are processed.
/// </summary>
enum class XTaskQueueDispatchMode : uint32_t
{
    /// <summary>
    /// Callbacks are invoked manually by XTaskQueueDispatch
    /// </summary>
    Manual,

    /// <summary>
    /// Callbacks are queued to the system thread pool and will
    /// be processed in order by the thread pool across multiple thread
    /// pool threads.
    /// </summary>
    ThreadPool,
    
    /// <summary>
    /// Callbacks are queued to the system thread pool and
    /// will be processed one at a time.
    /// </summary>
    SerializedThreadPool,
    
    /// <summary>
    /// Callbacks are not queued at all but are dispatched
    /// immediately by the thread that submits them.
    /// </summary>
    Immediate
};
```

**workDispatchMode** determina o modo de despacho da thread que processa o trabalho assíncrono, enquanto **completionDispatchMode** determina o modo de despacho da thread que processa a conclusão da operação assíncrona.

Depois de criar seu **XTaskQueueHandle**, basta adicioná-lo ao **XAsyncBlock** para controlar as threads das suas funções de trabalho e de conclusão.
Quando terminar de usar o **XTaskQueueHandle**, normalmente quando o jogo estiver terminando, você poderá fechá-lo com **XTaskQueueCloseHandle**:

```cpp theme={null}
STDAPI_(void) XTaskQueueCloseHandle(
    _In_ XTaskQueueHandle queue
    ) noexcept;
```

**Exemplo de chamada**:

```cpp theme={null}
XTaskQueueCloseHandle(queue);
```

### Despachando manualmente um **XTaskQueueHandle**

Se você usou o modo de despacho de fila manual para uma fila de trabalho ou de conclusão de um **XTaskQueueHandle**, precisará despachá-la manualmente.
Digamos que um **XTaskQueueHandle** tenha sido criado com a fila de trabalho e a fila de conclusão definidas para despacho manual, assim:

```cpp theme={null}
XTaskQueueHandle queue = nullptr;
HRESULT hr = XTaskQueueCreate(
    XTaskQueueDispatchMode::Manual,
    XTaskQueueDispatchMode::Manual,
    &queue);
```

Para despachar o trabalho que recebeu **XTaskQueueDispatchMode::Manual**, você precisará despachá-lo com a função **XTaskQueueDispatch**.

```cpp theme={null}
STDAPI_(bool) XTaskQueueDispatch(
    _In_ XTaskQueueHandle queue,
    _In_ XTaskQueuePort port,
    _In_ uint32_t timeoutInMs
    ) noexcept;
```

**Exemplo de chamada**

```cpp theme={null}
HRESULT hr = XTaskQueueDispatch(queue, XTaskQueuePort::Completion, 0);
```

* *queue* - a fila na qual o trabalho será despachado.
* *port* - uma instância do enum **XTaskQueuePort**.
* *timeoutInMs* - um uint32\_t para o tempo limite em milissegundos.

Há dois tipos de callback definidos pelo enum **XTaskQueuePort**:

```cpp theme={null}
/// <summary>
/// Declares which port of a task queue to dispatch or submit
/// callbacks to.
/// </summary>
enum class XTaskQueuePort : uint32_t
{
    /// <summary>
    /// Work callbacks
    /// </summary>
    Work,

    /// <summary>
    /// Completion callbacks after work is done
    /// </summary>
    Completion
};
```

### Quando chamar **XTaskQueueDispatch**

Para verificar quando a fila recebeu um novo item, você pode chamar **XTaskQueueRegisterMonitor** para definir um manipulador de eventos que informe ao seu código que há trabalhos ou conclusões prontos para serem despachados.

```cpp theme={null}
STDAPI XTaskQueueRegisterMonitor(
    _In_ XTaskQueueHandle queue,
    _In_opt_ void* callbackContext,
    _In_ XTaskQueueMonitorCallback* callback,
    _Out_ XTaskQueueRegistrationToken* token
    ) noexcept;
```

**XTaskQueueRegisterMonitor** recebe os seguintes parâmetros:

* *queue* - a fila assíncrona para a qual você está enviando o callback.
* *callbackContext* - um ponteiro para os dados que devem ser passados para o callback de envio.
* *callback* - a função que será invocada quando um novo callback for enviado para a fila.
* *token* - um token que será usado em uma chamada posterior a **XTaskQueueUnregisterMonitor** para remover o callback.

Por exemplo, aqui está uma chamada a **XTaskQueueRegisterMonitor**:

`XTaskQueueRegisterMonitor(queue, nullptr, HandleAsyncQueueCallback, &m_callbackToken);`

O callback **XTaskQueueMonitorCallback** correspondente pode ser implementado da seguinte forma:

```cpp theme={null}
void CALLBACK HandleAsyncQueueCallback(
    _In_opt_ void* context,
    _In_ XTaskQueueHandle queue,
    _In_ XTaskQueuePort port)
{
    switch (port)
    {
    case XTaskQueuePort::Work:
        {
            std::lock_guard<std::mutex> lock(g_workReadyMutex);
            g_workReady = true;
        }

        g_workReadyConditionVariable.notify_one(); // (std::condition_variable)
        break;
    }
}
```

Em seguida, em uma thread em segundo plano, você pode aguardar que essa variável de condição seja acionada e chamar **XTaskQueueDispatch**.

```cpp theme={null}
void BackgroundWorkThreadProc(XTaskQueueHandle queue)
{
    while (true)
    {
        {
            std::unique_lock<std::mutex> cvLock(g_workReadyMutex);
            g_workReadyConditionVariable.wait(cvLock, [] { return g_workReady; });

            if (g_stopBackgroundWork)
            {
                break;
            }

            g_workReady = false;
        }

        bool workFound = false;
        do
        {
            workFound = XTaskQueueDispatch(queue, XTaskQueuePort::Work, 0);
        } while (workFound);
    }
    
    XTaskQueueCloseHandle(queue);
}
```

## Confira também

[Introdução às APIs C da XSAPI](/pt-BR/services/xbox-services/fundamentals/xbox-services-api/live-xsapi-flat-c)

[Referência da XSAPI](/pt-BR/reference/live/xsapi-c/atoc-xsapi-c)

[libHttpClient](/pt-BR/reference/live/httpclient/atoc-httpclient)


## Related topics

- [API dos serviços do XBOX](/pt-BR/services/xbox-services/fundamentals/xbox-services-api/index.md)
- [Fazendo chamadas assíncronas](/pt-BR/services/playfab/sdks/c/async.md)
- [Fazendo chamadas assíncronas no SDK unificado do PlayFab](/pt-BR/services/playfab/sdks/unified-sdk/async-model.md)
- [Visão geral da XSAPI C](/pt-BR/services/xbox-services/fundamentals/xbox-services-api/live-xsapi-flat-c.md)
- [Modelo de programação assíncrona](/pt-BR/build/core-features/common/async/async-programming-model.md)
