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

# 在 XSAPI C API 中進行非同步呼叫

> 使用 XAsyncBlock 和 XTaskQueueHandle 控制 XBOX Live 遊戲中非同步 XSAPI C 呼叫 (例如 XblProfileGetUserProfileAsync) 的執行緒處理。

**非同步 API** 是一種會快速傳回但啟動**非同步工作**的 API，結果會在工作完成後傳回。

傳統上，使用**完成回呼**時，遊戲幾乎無法控制由哪個執行緒執行**非同步工作**，以及由哪個執行緒傳回結果。
有些遊戲的設計是讓堆積的某個區段只由單一執行緒存取，以避免任何執行緒同步處理的需求。
如果**完成回呼**不是從遊戲控制的執行緒呼叫，則以**非同步工作**的結果更新共用狀態時就需要執行緒同步處理。

XSAPI C API 公開了新的非同步 C API，讓開發人員在進行**非同步 API** 呼叫 (例如 **XblSocialGetSocialRelationshipsAsync()**、**XblProfileGetUserProfileAsync()** 和 **XblAchievementsGetAchievementsForTitleIdAsync()**) 時能直接控制執行緒。

以下是呼叫 **XblProfileGetUserProfileAsync** API 的基本範例：

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

若要了解此呼叫模式，您需要了解如何使用 **XAsyncBlock** 和 **XTaskQueueHandle**。

* **XAsyncBlock** 會攜帶與**非同步工作**和**完成回呼**相關的所有資訊。

* **XTaskQueueHandle** 可讓您決定由哪個執行緒執行**非同步工作**，以及由哪個執行緒呼叫 XAsyncBlock 的**完成回呼**。

## **XAsyncBlock**

讓我們詳細看看 **XAsyncBlock**。
它是定義如下的結構：

```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** 包含：

* *queue* - XTaskQueueHandle，這是代表要在何處執行某項工作之相關資訊的控制代碼。如果未設定，則會使用預設佇列。

* *context* - 可讓您將資料傳遞至回呼函式。

* *callback* - 選擇性的回呼函式，會在非同步工作完成後呼叫。如果您未指定回呼，可以使用 **XAsyncGetStatus** 等待 **XAsyncBlock** 完成，然後取得結果。

您應該為呼叫的每個非同步 API 在堆積上建立新的 XAsyncBlock。
XAsyncBlock 必須存留到呼叫 XAsyncBlock 的完成回呼為止，之後才能將其刪除。

> **重要：**
> **XAsyncBlock** 必須保留在記憶體中，直到**非同步工作**完成為止。如果它是以動態方式配置的，可以在 XAsyncBlock 的**完成回呼**內將其刪除。

### 等待**非同步工作**

您可以透過數種不同的方式得知**非同步工作**已完成：

* 呼叫了 XAsyncBlock 的**完成回呼**。
* 以 true 呼叫 **XAsyncGetStatus** 以等待其完成。

使用 **XAsyncGetStatus** 時，**非同步工作**會在 XAsyncBlock 的**完成回呼**執行之後視為完成，不過 XAsyncBlock 的**完成回呼**是選擇性的。

**非同步工作**完成後，您就可以取得結果。

### 取得**非同步工作**的結果

若要取得結果，大多數**非同步 API** 函式都有對應的 \[函式名稱]Result 函式，用來接收非同步呼叫的結果。

在我們的範例程式碼中，**XblProfileGetUserProfileAsync** 有對應的 **XblProfileGetUserProfileResult** 函式。
您可以使用此函式擷取函式的結果，並據以採取動作。

如需擷取結果的完整詳細資料，請參閱每個**非同步 API** 函式的文件。

## **XTaskQueueHandle**

**XTaskQueueHandle** 可讓您決定由哪個執行緒執行**非同步工作**，以及由哪個執行緒呼叫 XAsyncBlock 的**完成回呼**。

您可以設定*分派模式*，以控制由哪個執行緒執行這些作業。
可用的分派模式有三種：

* *Manual* - 手動佇列不會自動分派。由開發人員決定要在任何想要的執行緒上分派。這可用來將非同步呼叫的工作端或回呼端指派給特定執行緒。下文將更詳細地討論此模式。

* *Thread Pool* - 使用執行緒集區進行分派。執行緒集區會平行叫用呼叫，在執行緒集區執行緒可用時，依序從佇列取出要執行的呼叫。這最容易使用，但讓您對使用哪個執行緒的控制程度最低。

* *Serialized Thread Pool* - 使用執行緒集區進行分派。執行緒集區會循序叫用呼叫，在單一執行緒集區執行緒可用時，依序從佇列取出要執行的呼叫。

* *Immediate* - 在提交已排入佇列之工作的執行緒上立即分派該工作。

若要建立新的 **XTaskQueueHandle**，您需要呼叫 **XTaskQueueCreate**。
例如：

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

此函式接受兩個 `XTaskQueueDispatchMode` 參數。
`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** 決定處理非同步工作之執行緒的分派模式，而 **completionDispatchMode** 決定處理非同步作業完成之執行緒的分派模式。

建立 **XTaskQueueHandle** 之後，只要將其新增至 **XAsyncBlock**，即可控制工作函式和完成函式的執行緒處理。
當您使用完 **XTaskQueueHandle** 時 (通常是在遊戲結束時)，可以使用 **XTaskQueueCloseHandle** 將其關閉：

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

**呼叫範例**：

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

### 手動分派 **XTaskQueueHandle**

如果您對 **XTaskQueueHandle** 的工作佇列或完成佇列使用手動佇列分派模式，就需要手動分派。
假設建立了一個 **XTaskQueueHandle**，其工作佇列和完成佇列都設定為手動分派，如下所示：

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

若要分派已指派 **XTaskQueueDispatchMode::Manual** 的工作，您必須使用 **XTaskQueueDispatch** 函式進行分派。

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

**呼叫範例**

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

* *queue* - 要在哪個佇列上分派工作。
* *port* - **XTaskQueuePort** 列舉的執行個體。
* *timeoutInMs* - 以毫秒為單位之逾時的 uint32\_t。

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

### 何時呼叫 **XTaskQueueDispatch**

若要檢查佇列何時收到新項目，您可以呼叫 **XTaskQueueRegisterMonitor** 來設定事件處理常式，讓您的程式碼知道工作或完成已準備好分派。

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

**XTaskQueueRegisterMonitor** 接受下列參數：

* *queue* - 您要為其提交回呼的非同步佇列。
* *callbackContext* - 應傳遞至提交回呼之資料的指標。
* *callback* - 將新回呼提交至佇列時叫用的函式。
* *token* - 稍後呼叫 **XTaskQueueUnregisterMonitor** 以移除回呼時使用的權杖。

例如，以下是對 **XTaskQueueRegisterMonitor** 的呼叫：

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

對應的 **XTaskQueueMonitorCallback** 回呼可能會依下列方式實作：

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

接著在背景執行緒中，您可以接聽此條件變數以喚醒並呼叫 **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);
}
```

## 另請參閱

[XSAPI C API 簡介](/zh-TW/services/xbox-services/fundamentals/xbox-services-api/live-xsapi-flat-c)

[XSAPI 參考](/zh-TW/reference/live/xsapi-c/atoc-xsapi-c)

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


## Related topics

- [XBOX 服務 API](/zh-TW/services/xbox-services/fundamentals/xbox-services-api/index.md)
- [在 PlayFab Unified SDK 中進行非同步呼叫](/zh-TW/services/playfab/sdks/unified-sdk/async-model.md)
- [進行非同步呼叫](/zh-TW/services/playfab/sdks/c/async.md)
- [PlayFab Services SDK](/zh-TW/services/playfab/sdks/c/index.md)
- [Android 快速入門](/zh-TW/services/playfab/sdks/c/quickstart-android.md)
