Нужна помощь в асинхронном выполнении обновления приложения WPF - PullRequest
0 голосов
/ 22 сентября 2019

У меня проблемы с работой моей функции ComputeListener, как и ожидалось.

Я хочу, чтобы StatusBlock.Text немедленно обновлялся в пользовательском интерфейсе еще до того, как Compute () начинается, но яне могу заставить это работать.

Я знаю, что этот вопрос задавался в другом месте в StackOverflow, но ни одно из решений, которые я видел, не работало для меня.

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

Я также хочу иметь возможность нажатькнопка закрытия, чтобы остановить приложение, если Compute () занимает слишком много времени, но это также не может произойти.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public void ComputeListener(object sender, RoutedEventArgs args)
    {
        var t = Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { StatusBlock.Text = "Status: Processing"; })));
        t.Wait();
        Task.Run(() => Dispatcher.BeginInvoke(new Action(() => { Compute(); })));
    }
    private void Compute()
    {
       //code here
    }

Ответы [ 2 ]

1 голос
/ 22 сентября 2019

Похоже, вы правильно определили, что выполнение Compute() в потоке пользовательского интерфейса остановит изменение StatusBlock.Text от отображения в пользовательском интерфейсе по желанию, и что следует использовать Task.

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

Как рекомендует Icepickle - вы хотите async / await здесь.Я думаю, что должно работать следующее:

    public async void ComputeListener(object sender, RoutedEventArgs args)
    {
        StatusBlock.Text = "Status: Processing";
        await Task.Run(() => Compute());
    }

    private void Compute()
    {
       //code here
    }

Или даже лучше:

    public async void ComputeListener(object sender, RoutedEventArgs args)
    {
        StatusBlock.Text = "Status: Processing";
        await ComputeAsync();
    }

    private async Task ComputeAsync()
    {
       //async code here
    }

Примечание , если вашей реализации Compute() также необходим доступ к пользовательскому интерфейсу,с этим у вас все еще будут проблемы - это запах кода, поскольку ваша бизнес-логика (т. е. вычисления) не должна зависеть от внешнего интерфейса WPF.

Я бы порекомендовал перейти к подходу MVVM - это Документация Xamarin.Forms хорошо объясняет общие понятия, которые могут быть применены к WPF почти таким же образом

0 голосов
/ 22 сентября 2019

Вы должны использовать элемент асинхронного диспетчера Dispatcher.InvokeAsync вместо того, чтобы заключить его в Task.

Task.Run, всегда ожидающее выполнения его асинхронно.Task.Wait() противоречит идее, так как заставляет Task выполнять синхронно, так как он блокирует поток до тех пор, пока Task не завершится, не будет отменен или неисправен.Прочитайте замечания Microsoft Docs: Task.Wait.При использовании в сочетании с Task.Run высоки шансы для введения тупика.Поэтому всегда избегайте Task.Wait и используйте асинхронные и ожидаемые версии Task.WaitAny и Task.WaitAll, если вам нужно объединить несколько Task экземпляров.

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();
  }

  public async void ComputeListener(object sender, RoutedEventArgs args)
  {
    // Don't block while waiting for Dispatcher to execute the action
    await Dispatcher.InvokeAsync(() => StatusBlock.Text = "Status: Processing");

    // Executes asynchronously on a background thread (thread pool thread).
    // Use for CPU bound operations
    await ComputeOnNewBackgroundThreadAsync();

    // Executes asynchronously on the current thread.
    // Use for lightweight async operations 
    Task task = new Task(ComputeOnCurrentUiThread);
    task.Start();
    await task;

    // Waiting for tasks example
    var tasks = new List<Task>();
    Task currentThreadTask = new Task(ComputeOnCurrentUiThread);
    currentThreadTask.Start();
    tasks.Add(currentThreadTask);

    Task backgroundThreadTask = ComputeOnNewBackgroundThreadAsync();
    tasks.Add(backgroundThreadTask);

    // Wait asynchronously for both Task instances to complete
    await Task.WhenAll(tasks);
  }

  private async Task ComputeOnNewBackgroundThreadAsync()
  {
    await Task.Run(
      () =>
      {
        //code here
      });
  }

  private void ComputeOnCurrentUiThread()
  {
    //code here
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...