Delen via


Keep the UI thread responsive (XAML)

Users expect an app to remain responsive while it does computation, regardless of the type of machine. This means different things for different apps. For some this translates to providing more realistic physics, loading data from disk or the web faster, quickly presenting complex scenes and navigating between pages, finding directions in a snap, or rapidly processing data. Regardless of the type of computation, users want their app to act on their input and eliminate instances where it appears suspended while it “thinks.” Here we look at design choices you can make to keep your Windows Store app using C++, C#, or Visual Basic for Windows 8 responsive to user input while it performs advanced computation.

Windows Store apps using C++, C#, or Visual Basic are event-driven and all UI components share the same thread. Event-driven means that your code executes when an event occurs. After your app completes the work that is in response to an event, it sits idle, waiting for the next event to occur. The framework code for UI (running layout, processing input, firing events, and so on) and an app’s code for UI all are executed on the same thread. Only one piece of code can execute at a time, so if your app code takes too long to run, the framework can’t run layout or notify an app of other events that fired. In other words, the responsiveness of your app is directly related to the availability of the UI thread to process work. The less work you are doing on the UI thread, the more responsive your program will be to user input.

Note  Not all changes to the UI are necessarily done on the UI thread. There's a separate render thread that can apply UI changes that won't affect how input is handled or the basic layout. For example many animations and transitions that inform users that a UI action has taken place can actually run on this render thread. But if it's your code that is changing UI elements somewhere in your pages, it's best to assume that your code could have the potential to block the UI thread, unless you're familiar with those APIs or subsystems and know for certain they don't affect the UI thread.

 

Use asynchronous APIs

To help keep your app responsive, the platform provides asynchronous versions of many of its APIs. An asynchronous API ensures that your active execution thread never blocks for a significant amount of time. When you call an API from the UI thread, use the asynchronous version if it's available. For more info about programming with async patterns, see Asynchronous programming or Quickstart: Calling asynchronous APIs.

Offload work to background threads

In general, it is a good practice to write event handlers so that they finish quickly, but there are many legitimate cases where a non-trivial amount of work needs to be performed. If your code takes a perceivable amount of time to execute, then we recommend that you schedule the work on a background thread.

You can schedule work asynchronously by using the await operator in C#, the Await operator in Visual Basic, or delegates in C++. But this doesn't guarantee that the work you schedule will run on a background thread. Many of the Windows RuntimeAPIs schedule work in the background thread for you, but if you call your app code by using only await or a delegate, you run that delegate or method on the UI thread. You have to explicitly say when you want to run your app code on a background thread. In C#C# and Visual Basic you can accomplish this by passing code to Task.Run. The example shows how you might schedule work on a background thread.

An example of work that can be performed on a background thread is the calculating of computer AI in a game. Typically, the code that calculates the computer's next move takes a lot of time to execute. If this code is executed on the main thread, the app can't respond to additional interactions from the user until the code finishes executing. Instead, you can schedule that logic to execute on a background thread and then return to the UI thread after the computation is complete. Here's an example.

public class Example
{
    // ...
    private async void NextMove_Click(object sender, RoutedEventArgs e)
    {
        await Task.Run(() => ComputeNextMove());
        // Update the UI with results
    }

    private async Task ComputeNextMove()
    {
        // ...
    }
    // ...
}
Public Class Example
    ' ...
    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

In this example, ComputeNextMove executes on a background thread, and the NextMove_Click method waits until ComputeNextMove completes before continuing to update the UI with the results.

Note  There's also a ThreadPool and ThreadPoolTimer API for the Windows Runtime, which can be used for similar scenarios. That's not discussed here, for more info, see Using the thread pool in Windows Store apps.

 

Asynchronous programming

Quickstart: Calling asynchronous APIs

Responding to user interaction