概念模型
Microsoft Game Development Kit (GDK) 中的非同步程式設計分為 2 個主要元件: 工作和工作佇列。雖然程式庫中還有更多功能, 但整個概念模型都使用這 2 個主要元件。 工作是一組單一的非同步作業,可以啟動、檢查其 狀態、可能被取消、完成,並傳回其 完成資訊。在 Microsoft Game Development Kit (GDK) 模型中,工作由兩個 主體組成:工作回呼和完成回呼。這可提供 更多控制權,例如完全平行處理,或將平行工作 與單一執行緒完成結合。 工作佇列是一個容器,會將工作回呼和完成 回呼加入佇列以供稍後執行。工作佇列中有兩個內部佇列, 稱為連接埠,分別處理工作回呼和完成回呼。 這兩者稱為工作連接埠和完成連接埠。 圖 1. 工作和工作佇列的圖表

需求
遊戲開發人員列出了下列 API 呼叫需求。- 偏好同步呼叫而非非同步呼叫
- 提供具有輪詢的非同步
- 提供具有回呼的非同步
- 提供對非同步工作在哪個執行緒上執行的控制權
- 提供對完成回呼在哪個執行緒上執行的 控制權
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:
Internal 欄位由系統使用,不應修改。
在非同步作業期間,不應修改此結構中
使用者可設定的欄位。XAsyncBlock 必須在非同步作業的
存留期間保留在記憶體中。如果 XAsyncBlock 是動態配置的,
最早可在完成回呼中將其刪除。
除了 XAsyncBlock 之外,還有少數幾個協助程式 API,如下所示。
非同步 API 使用方式
首先,讓我們看看下列程式碼範例中的同步 API。控制工作分派
在先前的呼叫中,是哪個執行緒執行了非同步工作?是哪個執行緒叫用了 完成回呼?這取決於指派給 XAsyncBlock 的工作佇列。 工作佇列有兩個「連接埠」:工作連接埠和完成連接埠。每個 連接埠都有一個分派模式,決定如何處理加入連接埠佇列的回呼。 分派模式有好幾種。- 執行緒集區: 加入執行緒集區佇列的回呼會 在系統執行緒集區上執行。執行緒集區會平行叫用 這些呼叫,在執行緒集區執行緒可用時 依序從佇列中取出呼叫來執行。
- 序列化執行緒集區: 回呼會加入佇列並在 執行緒集區上執行,但一次只執行一個。
- 手動: 加入手動佇列的回呼不會自動 分派。由開發人員決定要在 哪個執行緒上分派它們。
- 立即: 立即分派模式完全不會加入佇列。它會 立即在提交回呼的執行緒上執行 該呼叫。
通知
通知可能沒有結束,而且可能會被呼叫許多次。 通知應支援非同步 呼叫需求的子集。- 具有輪詢的非同步
- 具有回呼的非同步
- 控制回呼在哪個執行緒上發生
- Register 方法,會接受任何呼叫特定的參數、工作 佇列、選用的 void 內容,以及強型別的回呼 指標。最後一個參數是輸出參數,會 傳回權杖。
- Unregister 方法,會接受任何呼叫特定的內容以及 權杖。
- 藉由新增與通知回呼無關的個別方法來 支援輪詢。
非同步程式庫
為了更容易建立支援非同步模式的一致 API, 我們提供了一個程式庫,可用來實作 API 的「非同步 管線」。此程式庫的 API 如下所示。- 使用呼叫端傳遞的非同步區塊呼叫 XAsyncBegin,並 提供一個提供實作的回呼。
- 執行呼叫的非同步工作。如果您需要在 背景工作執行緒上執行工作,請呼叫 XAsyncSchedule。如果您可以 使用 OS 非同步基本型別來執行工作,並能足夠快速地設定這些基本型別以 維持時間關鍵安全,則建議採用此方式。
- 如果您需要從背景工作執行緒回呼叫用其他非同步工作, 可以從背景工作傳回 E_PENDING。您也可以 從背景工作內部呼叫 XAsyncSchedule,以重新排程額外的 工作。
- 所有工作完成時,請呼叫 XAsyncComplete。
- 在 XAsyncGetResult 周圍提供強型別包裝函式,以傳回 結果。
- 如果您的非同步呼叫沒有資料承載,您應該在 XAsyncGetStatus 周圍提供強型別包裝函式,並將零作為所需 緩衝區大小傳遞給 XAsyncComplete。
- Begin 在 XAsyncBegin 期間,會以此 opcode 叫用非同步提供者。如果提供者實作此 op code,則應透過呼叫 XAsyncSchedule 或透過外部方式啟動其非同步工作。此回呼會在 XAsyncBegin 呼叫鏈中同步呼叫,因此絕不應封鎖。
- DoWork 在已呼叫 XAsyncSchedule 以 使用工作佇列排程非同步工作的情況下呼叫。提供者函式會執行 它需要的任何工作。完成時,它會使用 結果碼和資料承載大小呼叫 XAsyncComplete;如果呼叫沒有 資料承載,大小可以為零。如果需要完成更多非同步工作, 提供者可以排程該工作,並應傳回 E_PENDING。
- GetResult 呼叫以擷取呼叫的結果。由於 資料大小會在呼叫完成時傳入 XAsyncComplete,因此這裡 不需要檢查引數;所有緩衝區和緩衝區大小 都已由程式庫驗證。
- Cancel 在使用者取消非同步呼叫時呼叫。如果呼叫可以取消,請將其取消,並以 E_ABORT 作為結果碼呼叫 XAsyncComplete。
- Cleanup 在呼叫完全結束時呼叫,此時 提供者可以刪除任何動態記憶體。
