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

# 非同步程式設計模型

> 非同步程式設計模型

Microsoft Game Development Kit (GDK) 為非同步 API 實作了一種新模式，
以回應我們從遊戲開發人員收到、關於
XBOX One ERA 程式設計模型中所實作之非同步模式的意見反應。
我們的目標是讓這個新模式更容易整合到
一般的遊戲架構中，並為遊戲開發人員提供他們所要求的高度
控制權。本主題說明該設計
模式，並提出一個可用來
實作非同步模式的程式庫。

<a id="gdk_asynchronous_model" />

## 概念模型

Microsoft Game Development Kit (GDK) 中的非同步程式設計分為 2 個主要元件：
工作和工作佇列。雖然程式庫中還有更多功能，
但整個概念模型都使用這 2 個主要元件。

工作是一組單一的非同步作業，可以啟動、檢查其
狀態、可能被取消、完成，並傳回其
完成資訊。在 Microsoft Game Development Kit (GDK) 模型中，工作由兩個
主體組成：工作回呼和完成回呼。這可提供
更多控制權，例如完全平行處理，或將平行工作
與單一執行緒完成結合。

工作佇列是一個容器，會將工作回呼和完成
回呼加入佇列以供稍後執行。工作佇列中有兩個內部佇列，
稱為連接埠，分別處理工作回呼和完成回呼。
這兩者稱為工作連接埠和完成連接埠。

**圖 1.  工作和工作佇列的圖表**

<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="工作和工作佇列的圖表。" width="660" height="314" data-path="images/gdk/features/common/async_task_taskqueue_diagram.png" />

工作佇列的每個連接埠都會[在建立
時](/zh-TW/reference/system/xtaskqueue/functions/xtaskqueuecreate)以不同方式設定，以產生不同的回呼執行行為。例如，
工作連接埠可設定為非同步，而
完成連接埠可設定為在主執行緒上序列執行。
可以設定手動設定，以完全控制執行
行為。連接埠設定模式會在[下文說明](#controlling_work_dispatching)。

啟動非同步工作時，回呼不會立即加入
工作佇列。[非同步提供者](/zh-TW/build/core-features/common/async/async-libraries/async-library-xasyncprovider)
會處理狀態變更，以確保工作在完成回呼加入佇列並分派之前
先加入佇列並分派。

工作佇列本身不會直接處理執行緒。而是
依賴外部呼叫來[分派](/zh-TW/reference/system/xtaskqueue/functions/xtaskqueuedispatch)其連接埠。
外部呼叫會決定執行緒和並行行為。
工作佇列本身是完全安全執行緒的。

**圖 2.  在多個執行緒上分派連接埠**

<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="顯示在多個執行緒上分派連接埠的影像。" width="628" height="230" data-path="images/gdk/features/common/async_port_dispatch_multithread.png" />

基本上就是這樣！工作的回呼會加入工作
佇列的工作連接埠和完成連接埠，而該工作佇列會以某種方式
分派這些回呼。API 包含一整套
功能，可用來管理工作佇列、檢查回呼狀態、追蹤
工作資料、建立自訂工作處理等。

Microsoft Game Development Kit (GDK) 非同步 API 呼叫一律會在內部實作工作回呼，
而完成回呼一律是選用的。對於 Microsoft Game Development Kit (GDK) 非同步
呼叫以外的用法，您必須提供工作回呼。

## 需求

遊戲開發人員列出了下列 API 呼叫需求。

1. 偏好同步呼叫而非非同步呼叫
2. 提供具有輪詢的非同步
3. 提供具有回呼的非同步
4. 提供對非同步工作在哪個執行緒上執行的控制權
5. 提供對完成回呼在哪個執行緒上執行的
   控制權

## API 類型

Microsoft Game Development Kit (GDK) 致力於讓 API
設計非常直接明瞭。遊戲開發人員是微調程式碼以
充分利用硬體的專家。我們會盡可能
將控制權交給他們。API 實作可分為下列類型。

* **時間敏感安全：** 時間敏感安全 API 是可以
  在時間敏感執行緒上呼叫的 API。請注意，雖然這通常
  表示 API 很簡單或非常快速，但關鍵概念在於
  API 的效能特性是*一致的*。它們
  一律是同步的，永遠不需要非同步
  版本。這些 API 應記載為時間敏感安全。

* **非時間敏感安全：** 這些 API 從
  轉譯執行緒呼叫並不安全。其效能特性可能差異
  很大。大多數 API 都屬於此類別。

* **非同步：** 這些 API 本質上是非同步的，例如
  Web 服務呼叫。它們使用本主題中所述的
  非同步模式。非同步 API 在 Microsoft Game Development Kit (GDK) 中不像在 XBOX One ERA 程式設計模型中那麼常見；非同步
  API 通常會長時間執行且可取消。除了少數
  特定使用案例之外，非同步 API 都會有
  非時間關鍵安全的同步版本。呼叫非同步
  API 應一律是時間關鍵安全的。

* **通知：** 通知本質上是週期性的，沒有
  明確的結束。它們與非同步 API 相關，但由於
  其週期性本質，對開發人員而言，它們的外觀和行為應
  有所不同。註冊通知應一律是
  時間關鍵安全的。

## 非同步 API 模式

Microsoft Game Development Kit (GDK) 引進了一種通用的非同步 API 模式，Microsoft Game Development Kit (GDK) 元件可以使用此模式來提供一致的非同步支援。其
核心是一個類似 OVERLAPPED 的結構，稱為 [XAsyncBlock](/zh-TW/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];
};
```

[XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 是由呼叫端提供的結構。呼叫端會填入
此結構中的選用欄位，如下表所示。

| 欄位 | 描述 |
| - | - |
| **queue** | 工作佇列控制代碼，可控制由哪些執行緒執行非同步呼叫。如果此參數為 null，則會使用處理序工作佇列。如果處理序工作佇列已設定為 null，呼叫會失敗並傳回 E\_NO\_TASK\_QUEUE。 |
| **context** | 傳遞給回呼函式的選用內容指標。 |
| **callback** | 作業完成時呼叫的選用回呼函式。 |

**Internal** 欄位由系統使用，不應修改。
在非同步作業期間，不應修改此結構中
使用者可設定的欄位。[XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 必須在非同步作業的
存留期間保留在記憶體中。如果 [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 是動態配置的，
最早可在完成回呼中將其刪除。

除了 [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 之外，還有少數幾個協助程式 API，如下所示。

```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](/zh-TW/reference/system/xasync/functions/xasyncgetstatus) 會傳回非同步呼叫的狀態。呼叫
開始時，此狀態為 E\_PENDING。完成時，它會變更為 S\_OK 或
特定錯誤。如果呼叫已取消，則會傳回
E\_ABORT。

[XAsyncGetResultSize](/zh-TW/reference/system/xasync/functions/xasyncgetresultsize) 會傳回取得呼叫結果
所需的緩衝區大小。用來擷取結果的實際 API 會針對
每個非同步呼叫量身打造。

[XAsyncCancel](/zh-TW/reference/system/xasync/functions/xasynccancel) 可用來取消呼叫。取消與否取決於被取消的作業，可能會同步發生、非同步發生，或完全不發生。如果作業已取消，
[XAsyncGetResult](/zh-TW/reference/system/xasyncprovider/functions/xasyncgetresult)、[XAsyncGetResultSize](/zh-TW/reference/system/xasync/functions/xasyncgetresultsize) 或 [XAsyncGetStatus](/zh-TW/reference/system/xasync/functions/xasyncgetstatus) 會傳回 E\_ABORT。已取消的
呼叫會向 [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 的 [XAsyncCompletionRoutine](/zh-TW/reference/system/xasync/functions/xasynccompletionroutine) 參數發出訊號，並叫用其
回呼。

[XAsyncRun](/zh-TW/reference/system/xasync/functions/xasyncrun) 是一個協助程式方法，可以非同步執行任何程式碼。

### 非同步 API 使用方式

首先，讓我們看看下列程式碼範例中的同步 API。

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

此 API 會呼叫 Web 服務，以判斷遊戲存檔儲存空間
還剩下多少。為了新增非同步支援，我們宣告一對新的
API。

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

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

如果非同步呼叫已
啟動，[XGameSaveGetRemainingQuotaAsync](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) 會傳回 S\_OK (因為此 API 只有非同步版本，傳回
E\_PENDING 沒有意義)。[XGameSaveGetRemainingQuotaResult](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) 在呼叫完成之前
會傳回 E\_PENDING。

讓我們看看實際的運作方式，如下所示。

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

[XAsyncBlocks](/zh-TW/reference/system/xasync/structs/xasyncblock) 都需要工作佇列 (如下所述)，工作佇列會控制
非同步呼叫的執行位置和方式。如果未提供，則會使用
整個處理序的工作佇列。

請注意，[XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 必須在非同步呼叫的存留期間
保留在記憶體中。在此範例中，它是動態配置的，並在
完成回呼中刪除。它也可以儲存為全域或成員
變數。如果同時將相同的 [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 用於
多個非同步呼叫，會導致未定義的行為。

[XGameSaveGetRemainingQuotaResult](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) 會完成非同步呼叫的循環。
它會釋放非同步區塊中的內部資料，因此該區塊現在可以
用於新的呼叫。後續對
[XGameSaveGetRemainingQuotaResult](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult) 的呼叫都會失敗。
[XGameSaveGetRemainingQuotaAsync](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync) 和 [XGameSaveGetRemainingQuotaResult](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult)
也會在非同步區塊內配對；如果您
將某個非同步呼叫與另一個結果 API 不當搭配，就會發生錯誤。

如果非同步呼叫沒有資料承載，也就是只有 HRESULT 狀態
重要，請定義只接受非同步區塊的 **Result** 方法，如下所示。

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

<a id="controlling_work_dispatching" />

### 控制工作分派

在先前的呼叫中，是哪個執行緒執行了非同步工作？是哪個執行緒叫用了
完成回呼？這取決於指派給
[XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 的工作佇列。

工作佇列有兩個「連接埠」：*工作連接埠*和*完成連接埠*。每個
連接埠都有一個分派模式，決定如何處理加入連接埠佇列的回呼。
分派模式有好幾種。

* **執行緒集區：** 加入執行緒集區佇列的回呼會
  在系統執行緒集區上執行。執行緒集區會平行叫用
  這些呼叫，在執行緒集區執行緒可用時
  依序從佇列中取出呼叫來執行。

* **序列化執行緒集區：** 回呼會加入佇列並在
  執行緒集區上執行，但一次只執行一個。

* **手動：** 加入手動佇列的回呼不會自動
  分派。由開發人員決定要在
  哪個執行緒上分派它們。

* **立即：** 立即分派模式完全不會加入佇列。它會
  立即在提交回呼的執行緒上執行
  該呼叫。

系統已設定一個預設的處理序工作佇列，因此工作連接埠
和完成連接埠都會透過系統執行緒集區分派。如果
[XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock) 中未傳入佇列參數，就會使用此
處理序工作佇列。遊戲也可以停用處理序工作佇列，要求
必須將佇列傳入 [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock)。

我們預期許多開發人員會選擇手動分派模式，以
完全控制非同步工作和完成
回呼的執行時機和位置。

如需工作佇列的詳細資料，請參閱[非同步工作佇列設計](/zh-TW/build/core-features/common/async/async-task-queue-design)。

## 通知

通知可能沒有結束，而且可能會被呼叫許多次。
通知應支援非同步
呼叫需求的子集。

1. 具有輪詢的非同步
2. 具有回呼的非同步
3. 控制回呼在哪個執行緒上發生

通知會使用工作佇列，讓開發人員能夠控制
回呼執行緒，但除此之外不會使用非同步區塊；它們
的設計更像具有 **Register** 和 **Unregister**
方法的標準事件。

* **Register** 方法，會接受任何呼叫特定的參數、工作
  佇列、選用的 void 內容，以及強型別的回呼
  指標。最後一個參數是*輸出*參數，會
  傳回權杖。

* **Unregister** 方法，會接受任何呼叫特定的內容以及
  權杖。

* 藉由新增與通知回呼無關的個別方法來
  支援輪詢。

讓我們看看下列可擷取 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);
```

請注意，在此範例中，**UnregisterMessageAvailable** 會接受最後一個
"wait" 參數並傳回 bool。這可讓呼叫端決定在
呼叫正被叫用時如何處理取消註冊。

<a id="heading-7" />

## 非同步程式庫

為了更容易建立支援非同步模式的一致 API，
我們提供了一個程式庫，可用來實作 API 的「非同步
管線」。此程式庫的 API 如下所示。

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

此 API 使用單一回呼，並結合一個作業值來
指出呼叫 API 的原因。另外還有一個會隨著呼叫進行
而填入的單一資料結構。若要使用此 API，
請執行下列動作。

1. 使用呼叫端傳遞的非同步區塊呼叫 [XAsyncBegin](/zh-TW/reference/system/xasyncprovider/functions/xasyncbegin)，並
   提供一個提供實作的回呼。

2. 執行呼叫的非同步工作。如果您需要在
   背景工作執行緒上執行工作，請呼叫 [XAsyncSchedule](/zh-TW/reference/system/xasyncprovider/functions/xasyncschedule)。如果您可以
   使用 OS 非同步基本型別來執行工作，並能足夠快速地設定這些基本型別以
   維持時間關鍵安全，則建議採用此方式。

3. 如果您需要從背景工作執行緒回呼叫用其他非同步工作，
   可以從背景工作傳回 E\_PENDING。您也可以
   從背景工作內部呼叫 [XAsyncSchedule](/zh-TW/reference/system/xasyncprovider/functions/xasyncschedule)，以重新排程額外的
   工作。

4. 所有工作完成時，請呼叫 [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)。

5. 在 [XAsyncGetResult](/zh-TW/reference/system/xasyncprovider/functions/xasyncgetresult) 周圍提供強型別包裝函式，以傳回
   結果。

6. 如果您的非同步呼叫沒有資料承載，您應該在
   [XAsyncGetStatus](/zh-TW/reference/system/xasync/functions/xasyncgetstatus) 周圍提供強型別包裝函式，並將零作為所需
   緩衝區大小傳遞給 [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)。

非同步提供者回呼會以下列
作業叫用。

* **Begin** 在 [XAsyncBegin](/zh-TW/reference/system/xasyncprovider/functions/xasyncbegin) 期間，會以此 opcode 叫用非同步提供者。如果提供者實作此 op code，則應透過呼叫 [XAsyncSchedule](/zh-TW/reference/system/xasyncprovider/functions/xasyncschedule) 或透過外部方式啟動其非同步工作。此回呼會在 [XAsyncBegin](/zh-TW/reference/system/xasyncprovider/functions/xasyncbegin) 呼叫鏈中同步呼叫，因此絕不應封鎖。

* **DoWork** 在已呼叫 [XAsyncSchedule](/zh-TW/reference/system/xasyncprovider/functions/xasyncschedule) 以
  使用工作佇列排程非同步工作的情況下呼叫。提供者函式會執行
  它需要的任何工作。完成時，它會使用
  結果碼和資料承載大小呼叫 [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)；如果呼叫沒有
  資料承載，大小可以為零。如果需要完成更多非同步工作，
  提供者可以排程該工作，並應傳回
  E\_PENDING。

* **GetResult** 呼叫以擷取呼叫的結果。由於
  資料大小會在呼叫完成時傳入 [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)，因此這裡
  不需要檢查引數；所有緩衝區和緩衝區大小
  都已由程式庫驗證。

* **Cancel** 在使用者取消非同步呼叫時呼叫。如果呼叫可以取消，請將其取消，並以 E\_ABORT 作為結果碼呼叫 [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)。

* **Cleanup** 在呼叫完全結束時呼叫，此時
  提供者可以刪除任何動態記憶體。

非同步提供者只需要實作其所需的作業。例如，
沒有清除作業的不可取消非同步 IO 只需要
實作 **GetResult**。

以下是 **FactorialAsync** 方法的範例，此方法以非同步方式
實作階乘。

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

## 參考 API 文件

* [XAsync (API 內容)](/zh-TW/reference/system/xasync/xasync_members)
  * 函式
    * [XAsyncGetStatus](/zh-TW/reference/system/xasync/functions/xasyncgetstatus)
    * [XAsyncGetResultSize](/zh-TW/reference/system/xasync/functions/xasyncgetresultsize)
    * [XAsyncCancel](/zh-TW/reference/system/xasync/functions/xasynccancel)
    * [XAsyncCompletionRoutine](/zh-TW/reference/system/xasync/functions/xasynccompletionroutine)
    * [XAsyncRun](/zh-TW/reference/system/xasync/functions/xasyncrun)
  * 結構
    * [XAsyncBlock](/zh-TW/reference/system/xasync/structs/xasyncblock)
* [XAsyncProvider (API 內容)](/zh-TW/reference/system/xasyncprovider/xasyncprovider_members)
  * 函式
    * [XAsyncGetResult](/zh-TW/reference/system/xasyncprovider/functions/xasyncgetresult)
    * [XAsyncBegin](/zh-TW/reference/system/xasyncprovider/functions/xasyncbegin)
    * [XAsyncSchedule](/zh-TW/reference/system/xasyncprovider/functions/xasyncschedule)
    * [XAsyncComplete](/zh-TW/reference/system/xasyncprovider/functions/xasynccomplete)
* [XTaskQueue (API 內容)](/zh-TW/reference/system/xtaskqueue/xtaskqueue_members)
  * 函式
    * [xtaskqueuecreate](/zh-TW/reference/system/xtaskqueue/functions/xtaskqueuecreate)
    * [xtaskqueuedispatch](/zh-TW/reference/system/xtaskqueue/functions/xtaskqueuedispatch)
* [xgamesave (API 內容)](/zh-TW/reference/system/xgamesave/xgamesave_members)
  * 函式
    * [XGameSaveGetRemainingQuotaAsync](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaasync)
    * [XGameSaveGetRemainingQuotaResult](/zh-TW/reference/system/xgamesave/functions/xgamesavegetremainingquotaresult)

## 另請參閱

[非同步程式設計設計目標與改進](/zh-TW/build/core-features/common/async/async-whitepaper)
[非同步工作佇列設計](/zh-TW/build/core-features/common/async/async-task-queue-design)


## Related topics

- [Direct3D 12 程式設計指南](/zh-TW/build/gdk-and-engines/guides/directx-12-programming-guide.md)
- [Advanced Shader Delivery](/zh-TW/build/gdk-and-engines/guides/advanced-shader-delivery.md)
- [PlayFab Services C API 參考](/zh-TW/services/playfab/api-references/c/index.md)
- [開發者無障礙資源](/zh-TW/build/game-principles/accessibility/accessibility-resources.md)
- [非同期タスク キューの設計](/ja-jp/build/core-features/common/async/async-task-queue-design.md)
