Поделиться через


Сохранение отклика потока пользовательского интерфейса

Пользователи ожидают, что приложение будет реагировать во время выполнения вычислений независимо от типа компьютера. Это означает разные вещи для разных приложений. Для некоторых это приводит к обеспечению более реалистичной физики, загрузке данных с диска или в Интернете быстрее, быстрому представлению сложных сцен и переходу между страницами, поиску направлений в оснастке или быстрой обработке данных. Независимо от типа вычислений, пользователи хотят, чтобы приложение действовало на входных данных и устраняло экземпляры, в которых оно не отвечает, пока оно "думает".

Ваше приложение управляется событиями, что означает, что код выполняет работу в ответ на событие, а затем находится в состоянии простоя до следующего. Код платформы для пользовательского интерфейса (макет, входные данные, повышение событий и т. д.) и код приложения для пользовательского интерфейса выполняются в одном потоке пользовательского интерфейса. Только одна инструкция может выполняться в этом потоке одновременно, поэтому если код приложения занимает слишком много времени для обработки события, платформа не может запускать макет или создавать новые события, представляющие взаимодействие с пользователем. Скорость реагирования приложения связана с доступностью потока пользовательского интерфейса для обработки работы.

Необходимо использовать поток пользовательского интерфейса, чтобы внести почти все изменения в поток пользовательского интерфейса, включая создание типов пользовательского интерфейса и доступ к их членам. Вы не можете обновить пользовательский интерфейс из фонового потока, но вы можете опубликовать в нем сообщение с помощью CoreDispatcher.RunAsync , чтобы вызвать запуск кода там.

Примечание. Единственным исключением является отдельный обрабатывающий поток, который может применять изменения пользовательского интерфейса, не затрагивающие способ обработки ввода или базовый макет. Например, многие анимации и переходы, которые не влияют на макет, могут выполняться в этом потоке отрисовки.

Создание экземпляра элемента Delay

Некоторые из самых медленных этапов в приложении могут включать запуск и переключение представлений. Не делайте больше работы, чем необходимо, чтобы открыть пользовательский интерфейс, который пользователь видит изначально. Например, не создавайте пользовательский интерфейс для постепенно раскрываемого пользовательского интерфейса и содержимого всплывающих окон.

  • Создавайте экземпляры элементов задержки с помощью атрибута x:Load или x:DeferLoadStrategy.
  • Программное вставка элементов в дерево по запросу.

Очереди CoreDispatcher.RunIdleAsync используются для обработки потока пользовательского интерфейса, когда он не занят.

Использование асинхронных API

Чтобы обеспечить реагирование приложения, платформа предоставляет асинхронные версии многих его API. Асинхронный API гарантирует, что активный поток выполнения никогда не блокируется в течение значительного времени. При вызове API из потока пользовательского интерфейса используйте асинхронную версию, если она доступна. Дополнительные сведения о программировании с помощью асинхронных шаблонов см. в статье "Асинхронное программирование " или "Вызов асинхронных API" в C# или Visual Basic.

Отключение работы в фоновые потоки

Напишите обработчики событий, чтобы быстро вернуться. В случаях, когда требуется выполнить нетривиальный объем работы, запланируйте его в фоновом потоке и возврате.

Вы можете асинхронно планировать работу с помощью оператора await в C#, оператора Await в Visual Basic или делегатов в C++. Но это не гарантирует, что запланированная работа будет выполняться в фоновом потоке. Многие API-интерфейсы универсальная платформа Windows (UWP) планируют работу в фоновом потоке, но если вы вызываете код приложения только с помощью ожидания или делегата, вы запускаете этот делегат или метод в потоке пользовательского интерфейса. Необходимо явно сказать, когда вы хотите запустить код приложения в фоновом потоке. В C# и Visual Basic это можно сделать, передав код в метод Task.Run.

Помните, что к элементам пользовательского интерфейса можно обращаться только из потока пользовательского интерфейса. Используйте поток пользовательского интерфейса для доступа к элементам пользовательского интерфейса перед запуском фоновой работы и (или) используйте 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 обработчик возвращает значение в ожидании , чтобы обеспечить скорость реагирования потока пользовательского интерфейса. Но выполнение снова выбирается в этом обработчике после ComputeNextMove завершения (который выполняется в фоновом потоке). Оставшийся код в обработчике обновляет пользовательский интерфейс с результатами.

Примечание. Для UWP существуют также API-интерфейсы ThreadPool и ThreadPoolTimer, которые можно использовать для аналогичных сценариев. Дополнительные сведения см. в разделе "Потоки и асинхронное программирование".