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

# Realizar llamadas asincrónicas en la API de C de XSAPI

> Use XAsyncBlock y XTaskQueueHandle para controlar los subprocesos en las llamadas asincrónicas de la API de C de XSAPI, como XblProfileGetUserProfileAsync, en títulos de XBOX Live.

Una **API asincrónica** es una API que se devuelve rápidamente pero inicia una **tarea asincrónica**, y el resultado se devuelve cuando la tarea finaliza.

Tradicionalmente, los juegos han tenido poco control sobre qué subproceso ejecuta la **tarea asincrónica** y qué subproceso devuelve los resultados al usar una **devolución de llamada de finalización**.
Algunos juegos están diseñados de modo que una sección del montón (heap) solo la toca un único subproceso para evitar cualquier necesidad de sincronización de subprocesos.
Si la **devolución de llamada de finalización** no se llama desde un subproceso que el juego controla, actualizar el estado compartido con el resultado de una **tarea asincrónica** requerirá sincronización de subprocesos.

La API de C de XSAPI expone una nueva API de C asincrónica que ofrece a los desarrolladores control directo de los subprocesos al
realizar una llamada a una **API asincrónica**, como **XblSocialGetSocialRelationshipsAsync()**, **XblProfileGetUserProfileAsync()** y **XblAchievementsGetAchievementsForTitleIdAsync()**.

Este es un ejemplo básico de llamada a la 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 comprender este patrón de llamada, necesitará comprender cómo usar el **XAsyncBlock** y el **XTaskQueueHandle**.

* El **XAsyncBlock** contiene toda la información relativa a la **tarea asincrónica** y a la **devolución de llamada de finalización**.

* El **XTaskQueueHandle** le permite determinar qué subproceso ejecuta la **tarea asincrónica** y qué subproceso llama a la **devolución de llamada de finalización** del XAsyncBlock.

## El **XAsyncBlock**

Veamos en detalle el **XAsyncBlock**.
Es una estructura definida de la siguiente manera:

```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];
};
```

El **XAsyncBlock** contiene:

* *queue*: un XTaskQueueHandle, que es un identificador que representa información sobre dónde ejecutar un fragmento de trabajo. Si no se establece, se usará una cola predeterminada.

* *context*: le permite pasar datos a la función de devolución de llamada.

* *callback*: una función de devolución de llamada opcional a la que se llamará una vez realizado el trabajo asincrónico.  Si no especifica una devolución de llamada, puede esperar a que el **XAsyncBlock** se complete con **XAsyncGetStatus** y, a continuación, obtener los resultados.

Debería crear un nuevo XAsyncBlock en el montón por cada API asincrónica a la que llame.
El XAsyncBlock debe existir hasta que se llame a la devolución de llamada de finalización del XAsyncBlock y, a continuación, puede eliminarse.

> **Importante:**
> Un **XAsyncBlock** debe permanecer en memoria hasta que se complete la **tarea asincrónica**. Si se asigna dinámicamente, puede eliminarse dentro de la **devolución de llamada de finalización** del XAsyncBlock.

### Esperar a una **tarea asincrónica**

Puede saber que una **tarea asincrónica** se ha completado de varias maneras diferentes:

* Se llama a la **devolución de llamada de finalización** del XAsyncBlock.
* Llame a **XAsyncGetStatus** con true para esperar hasta que se complete.

Con **XAsyncGetStatus**, la **tarea asincrónica** se considera completa después de que se ejecute la **devolución de llamada de finalización** del XAsyncBlock; sin embargo, la **devolución de llamada de finalización** del XAsyncBlock es opcional.

Una vez completada la **tarea asincrónica**, puede obtener los resultados.

### Obtener el resultado de la **tarea asincrónica**

Para obtener el resultado, la mayoría de las funciones de **API asincrónicas** tienen una función \[Nombre de la función]Result correspondiente para recibir el resultado de la llamada asincrónica.

En nuestro código de ejemplo, **XblProfileGetUserProfileAsync** tiene una función **XblProfileGetUserProfileResult** correspondiente.
Puede usar esta función para recuperar el resultado de la función y actuar en consecuencia.

Para obtener todos los detalles sobre la recuperación de resultados, consulte la documentación de cada función de **API asincrónica**.

## El **XTaskQueueHandle**

El **XTaskQueueHandle** le permite determinar qué subproceso ejecuta la **tarea asincrónica** y qué subproceso llama a la **devolución de llamada de finalización** del XAsyncBlock.

Puede controlar qué subproceso realiza estas operaciones estableciendo un *modo de distribución*.
Hay tres modos de distribución disponibles:

* *Manual*: las colas manuales no se distribuyen automáticamente.  Corresponde al desarrollador distribuirlas en el subproceso que desee. Esto puede usarse para asignar el lado del trabajo o el de la devolución de llamada de una llamada asincrónica a un subproceso específico.  Esto se analiza con más detalle a continuación.

* *Grupo de subprocesos*: distribuye mediante un grupo de subprocesos.  El grupo de subprocesos invoca las llamadas en paralelo, tomando por turno una llamada de la cola para ejecutarla a medida que los subprocesos del grupo quedan disponibles.  Es el más fácil de usar, pero le da la menor cantidad de control sobre qué subproceso se usa.

* *Grupo de subprocesos serializado*: distribuye mediante un grupo de subprocesos.  El grupo de subprocesos invoca las llamadas en serie, tomando por turno una llamada de la cola para ejecutarla a medida que el único subproceso del grupo queda disponible.

* *Inmediato*: distribuye inmediatamente el trabajo en cola en el subproceso desde el que se envió.

Para crear un nuevo **XTaskQueueHandle**, deberá llamar a **XTaskQueueCreate**.
Por ejemplo:

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

Esta función toma dos parámetros `XTaskQueueDispatchMode`.
Hay tres valores posibles 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 el modo de distribución del subproceso que gestiona el trabajo asincrónico, mientras que **completionDispatchMode** determina el modo de distribución del subproceso que gestiona la finalización de la operación asincrónica.

Una vez creado su **XTaskQueueHandle**, simplemente agréguelo al **XAsyncBlock** para controlar los subprocesos de sus funciones de trabajo y de finalización.
Cuando termine de usar el **XTaskQueueHandle**, normalmente cuando el juego está finalizando, puede cerrarlo con **XTaskQueueCloseHandle**:

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

**Ejemplo de llamada**:

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

### Distribuir manualmente un **XTaskQueueHandle**

Si usó el modo de distribución de cola manual para la cola de trabajo o de finalización de un **XTaskQueueHandle**, tendrá que distribuir manualmente.
Supongamos que se creó un **XTaskQueueHandle** en el que tanto la cola de trabajo como la cola de finalización están configuradas para distribuirse manualmente, de esta manera:

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

Para distribuir el trabajo al que se le ha asignado **XTaskQueueDispatchMode::Manual**, tendrá que distribuirlo con la función **XTaskQueueDispatch**.

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

**Ejemplo de llamada**

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

* *queue*: la cola en la que se distribuye el trabajo.
* *port*: una instancia de la enumeración **XTaskQueuePort**.
* *timeoutInMs*: un uint32\_t para el tiempo de espera en milisegundos.

Hay dos tipos de devolución de llamada definidos por la enumeración **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
};
```

### Cuándo llamar a **XTaskQueueDispatch**

Para comprobar cuándo la cola ha recibido un nuevo elemento, puede llamar a **XTaskQueueRegisterMonitor** para establecer un controlador de eventos que informe a su código de que hay trabajo o finalizaciones listos para ser distribuidos.

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

**XTaskQueueRegisterMonitor** toma los siguientes parámetros:

* *queue*: la cola asincrónica para la que envía la devolución de llamada.
* *callbackContext*: un puntero a los datos que deben pasarse a la devolución de llamada de envío.
* *callback*: la función que se invocará cuando se envíe una nueva devolución de llamada a la cola.
* *token*: un token que se usará en una llamada posterior a **XTaskQueueUnregisterMonitor** para quitar la devolución de llamada.

Por ejemplo, esta es una llamada a **XTaskQueueRegisterMonitor**:

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

La devolución de llamada **XTaskQueueMonitorCallback** correspondiente podría implementarse de la siguiente manera:

```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;
    }
}
```

Después, en un subproceso en segundo plano, puede escuchar esta variable de condición para reactivarse y llamar a **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);
}
```

## Consulte también

[Introducción a las API de C de XSAPI](/services/xbox-services/fundamentals/xbox-services-api/live-xsapi-flat-c)

[Referencia de XSAPI](/reference/live/xsapi-c/atoc-xsapi-c)

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


## Related topics

- [API de servicios XBOX](/es/services/xbox-services/fundamentals/xbox-services-api/index.md)
- [Realización de llamadas asincrónicas en el SDK unificado de PlayFab](/es/services/playfab/sdks/unified-sdk/async-model.md)
- [Modelo de programación asincrónica](/es/build/core-features/common/async/async-programming-model.md)
- [Canjear compras desde la aplicación del App Store de Apple](/es/services/playfab/economy-monetization/economy-v2/marketplace/marketplace-redemption/apple.md)
- [Realización de llamadas asincrónicas](/es/services/playfab/sdks/c/async.md)
