【问题标题】:Update the WPF UI while processing - best use of async await处理时更新 WPF UI - 异步等待的最佳使用
【发布时间】:2018-02-24 03:38:58
【问题描述】:

这是我尝试过的 - 就刷新 UI 而言,它可以工作,但我认为这不是 async / await 的最佳用途。我该如何改进?

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            DoSomeProcessingOnTheUIThread(reader);

            //only update UI every 100 loops
            if (rowCount > 0 && rowCount % 100 == 0)
            {
                //yay! release the UI thread
                await Task.Run(() =>
                {
                    Application.Current.Dispatcher.Invoke(
                        () =>
                        {
                            txtRowCount.Text = string.Format("{0}", rowCount);
                        });
                });
            }

        } //end-while
    }//end-using
}

有什么更好的方法?

更新: 我根据 Clemens 的回答避免了 Dispatcher.Invoke,方法是将处理发送到后台任务,并直接在 UI 上更新进度。 我的代码现在看起来像。

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            await DoSomeProcessing(reader);

            //only update UI every 100 loops
            if (rowCount % 100 == 0)
            {
                txtRowCount.Text = string.Format("{0}", rowCount);
            }

        } //end-while
    }//end-using
    MessageBox.Show("I am done!");
}

private Task DoSomeProcessing(XmlReader reader)
{
   Task t =Task.Run(() =>
   {
       //Do my processing here.
   });
   return t;
}

更新#2: 反思一下,为什么我要在每个循环上创建一个新任务? 在一个后台任务中运行整个循环可能会更好。并定期发起回调以显示进度;请参阅下面的其他答案。

【问题讨论】:

  • 如果您的代码有效,但您希望有人提出改进建议,您可能想在 Code Review 上提出这个问题。
  • 您是否应该在非 UI 线程上进行“一些处理”?
  • DoSomeProcessingOnTheUIThread 是做什么的?
  • DoSomeProcessingOnTheUIThread() 只是我必须对 XML 文件进行的一些处理;即我要处理的“肉”,我很懒惰,并试图避免回调 UI 线程以通知进度。
  • 您更新的代码看起来不错。附言。次要的,习惯上用“Async”来结束异步运行的方法的名称;等待 DoSomeProcessingAsync()。

标签: wpf async-await task-parallel-library


【解决方案1】:

立即调用 Dispatcher.Invoke 的 Task 毫无意义,因为除了调度 Dispatcher 操作的一小段代码之外,实际上没有任何东西在后台线程上运行。

最好直接设置Text属性,使用XmlReader.ReadAsync

private async void btnQuickTest_Click(object sender, RoutedEventArgs e)
{
    using (var reader = XmlReader.Create("someXmlFile.xml"))
    {
        int rowCount = 0;

        while (await reader.ReadAsync())
        {
            rowCount++;

            await Task.Run(() =>
            {
                DoSomeWork(reader);
            });

            if (rowCount > 0 && rowCount % 100 == 0)
            {
                txtRowCount.Text = string.Format("{0}", rowCount);
            }
        }
    }
}

你也可以考虑让你的 DoSomeWork 异步并直接这样调用它:

await DoSomeWork(reader);

【讨论】:

  • 我不明白 .ReadAsync() 将如何帮助我;我想继续阅读文件并处理其中的记录,并定期在 UI 上显示进度而不阻塞它(或让行数从 0 变为完成)。但是您等待处理部分的第二个想法对我有所帮助。所以我改变了它,因此不需要 Dispatcher - 更新了我的问题,但无论如何你应该得到答案。
  • ReadAsync 在后台线程中处理 XML 流,而 Read 在 UI 线程中运行(此处)。虽然此处的性能差异可能是可以接受的,但如果您已经处于可等待方法中,调用等效的 Async 方法总是一个好主意。
【解决方案2】:

我的最终答案。

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

    await Task.Run(() => DoSomeWorkOnBgThread( (cnt) => //HAHA! this syntax is so confusing
        {
            Application.Current.Dispatcher.Invoke(() =>
                {
                    txtRowCount.Text = cnt.ToString();
                }
            ); //end-invoke
        }
    ));

    MessageBox.Show("i am done!");
}


private void DoSomeWorkOnBgThread(Action<int> callbackFn)
{

    XmlReader reader;
    int rowCount = 0;
    using (reader = XmlReader.Create("someXmlFile.xml"))
    {
        while (reader.Read())
        {
            rowCount++;

            DoMyProcessingHere();

            //only update UI every 100 loops
            if (rowCount % 100 == 0)
            {
                callbackFn(rowCount); ///
            }

        } //end-while
    }//end-using
}

【讨论】:

  • 最好使用IProgress&lt;string&gt;Progress&lt;string&gt; 而不是Dispatcher.Invoke
猜你喜欢
  • 1970-01-01
  • 2017-11-22
  • 1970-01-01
  • 2013-02-02
  • 1970-01-01
  • 2014-08-08
  • 2022-01-02
  • 1970-01-01
  • 2021-11-01
相关资源
最近更新 更多