讓 UI 執行緒保持回應
不論使用何種電腦,使用者都希望應用程式在進行計算時仍然能夠回應。 這對於不同的應用程式有不同的意義。 對某些應用程式而言,這表示提供更實際的物理性質、更快地從磁碟或網路載入資料、快速地呈現複雜的場景和在頁面之間瀏覽、立即找到方向,或是快速處理資料。 不論執行何種計算,使用者都會希望應用程式可以回應輸入,不希望有看似在「思考」而停止回應的情況發生。
您的應用程式是事件驅動,這表示程式碼會執行工作來回應事件,接著進入閒置狀態,直到出現下一個事件。 平台的 UI 程式碼 (配置、輸入、引發事件等等) 及應用程式的 UI 程式碼都在相同的 UI 執行緒上執行。 該執行緒上一次只能執行一個指令,如果您的應用程式程式碼花費太多時間處理事件,則架構無法執行配置或引發代表使用者互動的新事件。 應用程式的回應性與是否有 UI 執行緒來處理工作相關。
幾乎所有對 UI 執行緒所做的變更都需要用到 UI 執行緒,包括建立 UI 類型和存取其成員。 您無法從背景執行緒更新 UI,但可以使用 CoreDispatcher.RunAsync 傳遞訊息給它,就在那裡執行程式碼。
注意例外的是另有一個轉譯執行緒可以套用 UI 變更,而不會影響輸入的處理方式或基本配置。 例如,許多不會影響版面配置的動畫和轉場效果可以在這個轉譯執行緒上執行。
延遲元素具現化
應用程式中最慢的一些階段包括啟動和切換檢視。 顯示使用者最初看到的 UI 時不要太過花俏。 例如,不要建立那種逐漸展露 UI 和快顯內容的 UI。
- 使用 x:Load 屬性或 x: DeferLoadStrategy 延遲具現化元素。
- 以程式設計方式隨需將元素插入樹狀目錄。
CoreDispatcher.RunIdleAsync 佇列適用於 UI 執行緒不忙碌時處理。
使用非同步 API
為了協助保持應用程式的回應性,平台為其許多 API 提供非同步的版本。 非同步 API 可確保使用中的執行緒不會被封鎖太長的時間。 當您從 UI 執行緒呼叫 API 時,如果有非同步版本可用,請使用非同步版本。 如需使用 async 模式進行設計程式的詳細資訊,請參閱非同步程式設計或在 C# 或 Visual Basic 中呼叫非同步 API。
將工作卸載到背景執行緒
撰寫可快速返回的事件處理常式。 在需要執行不少的工作量時,排定給背景執行緒並返回。
您可以使用 C# 的 await 運算子、Visual Basic 的 Await 運算子或 C++ 中的委派,以非同步方式排程工作。 但這並不保證您排程的工作一定會在背景執行緒中執行。 許多通用 Windows 平台 (UWP) API 會為您在背景執行緒中排程工作,但如果僅使用 await 或委派呼叫您的應用程式程式碼,則會在 UI 執行緒中執行該委派或方法。 您必須明確指示何時要在背景執行緒中執行您的應用程式程式碼。 在 C# 與 Visual Basic 中,將程式碼傳送到 Task.Run 即可做出明確的指示。
請記住,只有從 UI 執行緒才能存取 UI 元素。 啟動背景工作之前先使用 UI 執行緒存取 UI 元素,以及/或在背景執行緒上使用 CoreDispatcher.RunAsync 或 CoreDispatcher.RunIdleAsync。
例如,遊戲中的人工智慧計算,就是背景執行緒中可以執行的工作。 計算電腦下一步行動的程式碼需要花很多時間執行。
public class AsyncExample
{
private async void NextMove_Click(object sender, RoutedEventArgs e)
{
// The await causes the handler to return immediately.
await System.Threading.Tasks.Task.Run(() => ComputeNextMove());
// Now update the UI with the results.
// ...
}
private async System.Threading.Tasks.Task ComputeNextMove()
{
// Perform background work here.
// Don't directly access UI elements from this method.
}
}
Public Class AsyncExample ' ... Private Async Sub NextMove_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Await Task.Run(Function() ComputeNextMove()) ' update the UI with results End Sub Private Async Function ComputeNextMove() As Task ' ... End Function ' ... End Class
在這個範例中,NextMove_Click
處理常式會回到 await,讓 UI 執行緒保持回應。 但在 ComputeNextMove
(在背景執行緒上執行) 完成之後,該處理常式又會再次執行。 處理常式中的其餘程式碼會以結果更新 UI。
注意UWP 還有 ThreadPool 和 ThreadPoolTimer API 可用於類似的案例。 如需詳細資訊,請參閱執行緒和非同步程式設計。