Как запустить асинхронную задачу в WPF без зависания gui

#c# #wpf #task #invoke #dispatcher

#c# #wpf #задача #вызвать #диспетчер

Вопрос:

У меня есть приложение WPF для мониторинга данных, и я пишу Task.Run в загруженном методе для выполнения работы и отображения данных в графическом интерфейсе.

Код выглядит следующим образом:

 private async void Window_Loaded(object sender, RoutedEventArgs e)
{

        await Task.Run(() => DoAddAsync());
}

void DoAddAsync()
{

  while (true)

  {
       this.Dispatcher.Invoke(() =>
       {
              //Do Some
       });
  }
}
  

Это работает нормально, и я могу изменить компонент в графическом интерфейсе пользователя без зависания.

Но если я выполняю метод в this.Dispatcher.Invoke , графический интерфейс зависает, и я ничего не могу сделать, пока этот метод не закроется.

Кто-нибудь может помочь мне запустить метод в this.Dispatcher.Invoke без зависания GUI?

Комментарии:

1. while (true) с помощью only Dispatcher.Invoke бессмысленно, поскольку весь ваш код все еще выполняется в потоке пользовательского интерфейса. Выполняйте фактические асинхронные действия снаружи Invoke и обновляйте элементы пользовательского интерфейса только с помощью Invoke .

2. Или, что еще лучше, используйте IProgress<T> вместо Dispatcher.Invoke .

Ответ №1:

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

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

 private async Task DoAddAsync()
{
    while (true)
    {
        // do the work in the loop
        // ....
        // don't run again for at least 200 milliseconds
        await Task.Delay(200);
    }
}
  

Однако реальная хитрость в том, чтобы заставить это работать, заключается не в методе DoAddAsync. Так мы это называем. Вы не ожидаете вызова к ней.

 private async void Window_Loaded(object sender, RoutedEventArgs e)
{
     DoAddAsync()
}
  

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

 private async void Window_Loaded(object sender, RoutedEventArgs e)
{
    await Task.Run(async () =>
    {
        while (true)
        {
            // do the work in the loop

            // update the UI on the UI thread
            Dispatcher.Invoke(() => // do some );

            // don't run again for at least 200 milliseconds
            await Task.Delay(200);
        }
    });
}
  

Комментарии:

1. Ваше предложение работает нормально. но моя проблема заключается в вызове метода в цикле while. Можете сказать мне, как определить метод, который я могу вызывать в цикле while без зависания. это моя настоящая проблема

2. вы пытались добавить await Task.Delay(200); ?