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

# Effectuer des appels asynchrones dans l'API C de XSAPI

> Utilisez XAsyncBlock et XTaskQueueHandle pour contrôler les threads des appels asynchrones de l'API C de XSAPI, comme XblProfileGetUserProfileAsync, dans les titres XBOX Live.

Une **API asynchrone** est une API qui rend la main rapidement, mais qui démarre une **tâche asynchrone**; le résultat est retourné une fois la tâche terminée.

Traditionnellement, les jeux disposaient de peu de contrôle sur le thread qui exécute la **tâche asynchrone** et sur celui qui retourne les résultats lors de l'utilisation d'un **rappel d'achèvement**.
Certains jeux sont conçus de sorte qu'une section du tas ne soit manipulée que par un seul thread, afin d'éviter tout besoin de synchronisation des threads.
Si le **rappel d'achèvement** n'est pas appelé depuis un thread contrôlé par le jeu, la mise à jour de l'état partagé avec le résultat d'une **tâche asynchrone** nécessite une synchronisation des threads.

L'API C de XSAPI expose une nouvelle API C asynchrone qui offre aux développeurs un contrôle direct des threads lors
d'un appel d'**API asynchrone**, comme **XblSocialGetSocialRelationshipsAsync()**, **XblProfileGetUserProfileAsync()** et **XblAchievementsGetAchievementsForTitleIdAsync()**.

Voici un exemple simple d'appel de l'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);
```

Pour comprendre ce modèle d'appel, vous devez savoir comment utiliser **XAsyncBlock** et **XTaskQueueHandle**.

* **XAsyncBlock** contient toutes les informations relatives à la **tâche asynchrone** et au **rappel d'achèvement**.

* **XTaskQueueHandle** vous permet de déterminer quel thread exécute la **tâche asynchrone** et quel thread appelle le **rappel d'achèvement** de XAsyncBlock.

## **XAsyncBlock**

Examinons **XAsyncBlock** en détail.
Il s'agit d'une structure définie comme suit :

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

**XAsyncBlock** contient :

* *queue* : un XTaskQueueHandle, c'est-à-dire un handle représentant des informations sur l'endroit où exécuter une unité de travail. S'il n'est pas défini, une file d'attente par défaut est utilisée.

* *context* : vous permet de transmettre des données à la fonction de rappel.

* *callback* : une fonction de rappel facultative qui est appelée une fois le travail asynchrone terminé. Si vous ne spécifiez pas de rappel, vous pouvez attendre la fin de **XAsyncBlock** avec **XAsyncGetStatus**, puis obtenir les résultats.

Vous devez créer un nouveau XAsyncBlock sur le tas pour chaque API asynchrone que vous appelez.
Le XAsyncBlock doit rester en vie jusqu'à ce que son rappel d'achèvement soit appelé; il peut ensuite être supprimé.

> **Important :**
> Un **XAsyncBlock** doit rester en mémoire jusqu'à la fin de la **tâche asynchrone**. S'il est alloué dynamiquement, il peut être supprimé dans le **rappel d'achèvement** de XAsyncBlock.

### Attendre une **tâche asynchrone**

Vous pouvez savoir qu'une **tâche asynchrone** est terminée de plusieurs façons :

* Le **rappel d'achèvement** de XAsyncBlock est appelé.
* Appelez **XAsyncGetStatus** avec la valeur true pour attendre sa fin.

Avec **XAsyncGetStatus**, la **tâche asynchrone** est considérée comme terminée après l'exécution du **rappel d'achèvement** de XAsyncBlock; toutefois, ce **rappel d'achèvement** est facultatif.

Une fois la **tâche asynchrone** terminée, vous pouvez obtenir les résultats.

### Obtenir le résultat de la **tâche asynchrone**

Pour obtenir le résultat, la plupart des fonctions d'**API asynchrones** disposent d'une fonction \[Nom de la fonction]Result correspondante qui reçoit le résultat de l'appel asynchrone.

Dans notre exemple de code, **XblProfileGetUserProfileAsync** dispose d'une fonction **XblProfileGetUserProfileResult** correspondante.
Vous pouvez utiliser cette fonction pour récupérer le résultat de la fonction et agir en conséquence.

Pour plus de détails sur la récupération des résultats, consultez la documentation de chaque fonction d'**API asynchrone**.

## **XTaskQueueHandle**

**XTaskQueueHandle** vous permet de déterminer quel thread exécute la **tâche asynchrone** et quel thread appelle le **rappel d'achèvement** de XAsyncBlock.

Vous pouvez contrôler le thread qui effectue ces opérations en définissant un *mode de distribution*.
Les modes de distribution suivants sont disponibles :

* *Manual* : la file d'attente manuelle n'est pas distribuée automatiquement. C'est au développeur de distribuer les éléments sur le thread de son choix. Ce mode permet d'affecter le travail ou le rappel d'un appel asynchrone à un thread spécifique. Il est décrit plus en détail ci-dessous.

* *Thread Pool* : distribue à l'aide d'un pool de threads. Le pool de threads appelle les éléments en parallèle, en prenant à tour de rôle un appel à exécuter dans la file d'attente à mesure que des threads du pool deviennent disponibles. C'est le mode le plus simple à utiliser, mais c'est celui qui vous donne le moins de contrôle sur le thread utilisé.

* *Serialized Thread Pool* : distribue à l'aide d'un pool de threads. Le pool de threads appelle les éléments en série, en prenant à tour de rôle un appel à exécuter dans la file d'attente à mesure que l'unique thread du pool devient disponible.

* *Immediate* : distribue immédiatement le travail mis en file d'attente sur le thread à partir duquel il a été soumis.

Pour créer un nouveau **XTaskQueueHandle**, vous devez appeler **XTaskQueueCreate**.
Par exemple :

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

Cette fonction accepte deux paramètres `XTaskQueueDispatchMode`.
Les valeurs possibles de `XTaskQueueDispatchMode` sont les suivantes :

```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** détermine le mode de distribution du thread qui gère le travail asynchrone, tandis que **completionDispatchMode** détermine le mode de distribution du thread qui gère l'achèvement de l'opération asynchrone.

Une fois votre **XTaskQueueHandle** créé, ajoutez-le simplement au **XAsyncBlock** pour contrôler les threads de vos fonctions de travail et d'achèvement.
Lorsque vous avez fini d'utiliser le **XTaskQueueHandle**, généralement à la fermeture du jeu, vous pouvez le fermer avec **XTaskQueueCloseHandle** :

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

**Exemple d'appel** :

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

### Distribution manuelle d'un **XTaskQueueHandle**

Si vous avez utilisé le mode de distribution manuelle pour la file d'attente de travail ou d'achèvement d'un **XTaskQueueHandle**, vous devez effectuer la distribution manuellement.
Supposons qu'un **XTaskQueueHandle** ait été créé avec la file d'attente de travail et la file d'attente d'achèvement toutes deux définies sur une distribution manuelle, comme ceci :

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

Pour distribuer le travail auquel **XTaskQueueDispatchMode::Manual** a été affecté, vous devez utiliser la fonction **XTaskQueueDispatch**.

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

**Exemple d'appel**

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

* *queue* : la file d'attente sur laquelle distribuer le travail.
* *port* : une instance de l'énumération **XTaskQueuePort**.
* *timeoutInMs* : un uint32\_t indiquant le délai d'expiration en millisecondes.

L'énumération **XTaskQueuePort** définit deux types de rappels :

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

### Quand appeler **XTaskQueueDispatch**

Pour savoir quand la file d'attente a reçu un nouvel élément, vous pouvez appeler **XTaskQueueRegisterMonitor** afin de définir un gestionnaire d'événements qui indique à votre code que du travail ou des achèvements sont prêts à être distribués.

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

**XTaskQueueRegisterMonitor** accepte les paramètres suivants :

* *queue* : la file d'attente asynchrone pour laquelle vous soumettez le rappel.
* *callbackContext* : un pointeur vers les données à transmettre au rappel de soumission.
* *callback* : la fonction appelée lorsqu'un nouveau rappel est soumis à la file d'attente.
* *token* : un jeton utilisé ultérieurement lors d'un appel à **XTaskQueueUnregisterMonitor** pour supprimer le rappel.

Par exemple, voici un appel à **XTaskQueueRegisterMonitor** :

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

Le rappel **XTaskQueueMonitorCallback** correspondant peut être implémenté comme suit :

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

Ensuite, dans un thread d'arrière-plan, vous pouvez attendre le réveil de cette variable de condition et appeler **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);
}
```

## Voir aussi

[Introduction aux API C de XSAPI](/fr-CA/services/xbox-services/fundamentals/xbox-services-api/live-xsapi-flat-c)

[Référence XSAPI](/fr-CA/reference/live/xsapi-c/atoc-xsapi-c)

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


## Related topics

- [API des services XBOX](/fr-CA/services/xbox-services/fundamentals/xbox-services-api/index.md)
- [Effectuer des appels asynchrones dans le SDK unifié PlayFab](/fr-CA/services/playfab/sdks/unified-sdk/async-model.md)
- [Effectuer des appels asynchrones](/fr-CA/services/playfab/sdks/c/async.md)
- [Vue d'ensemble de l'API des services XBOX](/fr-CA/services/xbox-services/fundamentals/xbox-services-api/live-introduction-to-xbox-live-apis.md)
- [Fonction XAsyncProvider](/fr-CA/reference/system/xasyncprovider/functions/xasyncprovider.md)
