处理时更新 WPF UI - 异步等待的最佳使用

joe*_*not 4 wpf task-parallel-library async-await

这是我尝试过的 - 就看到 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
}
Run Code Online (Sandbox Code Playgroud)

什么是更好的方法?

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

Cle*_*ens 5

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

最好直接设置 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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您还可以考虑使DoSomeWork异步并直接调用它,如下所示:

await DoSomeWork(reader);
Run Code Online (Sandbox Code Playgroud)