2017-09-15 53 views
2

以下是我所尝试的 - 它的工作原理,只要看到用户界面刷新,但我不认为它是最佳使用异步/等待。我该如何改进?在处理时更新WPF 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++; 

      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 
} 

什么是更好的方法?

UPDATE: 我避免基于克莱门斯的答案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: 在反思,为什么我在每个循环创建一个新的任务? 在一个后台任务中运行整个循环可能会更好。并定期提出回调以显示进度;见下面我的其他答案。

+3

如果您的代码正常工作,但您希望某人提出改进建议,您可能需要在代码复审中提出此问题。 –

+0

不应该在非UI线程上进行“一些处理”? – Fredrik

+0

DoSomeProcessingOnTheUIThread做什么? – Fredrik

回答

4

立即调用Dispatcher.Invoke的任务是毫无意义的,因为除了调度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); 
+0

我没有看到.ReadAsync()会如何帮助我;我想继续阅读文件并处理其中的记录,并定期在UI上显示进度而不会阻止它(或使行数从0到完成)。但是等待处理部分的第二个想法帮助了我。所以我改变了它,所以没有调度员是必需的 - 更新我的问题,但你应该得到答案。 – joedotnot

+0

ReadAsync在后台线程中处理XML流,而Read在UI线程中运行(此处)。尽管这里的性能差异可能是可以接受的,但如果您已经处于等待方法中,那么调用Async方法是一个好主意。 – Clemens

0

我最后的答案。

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(count); 
      } 

     } //end-while 
    }//end-using 
} 
+0

最好使用'IProgress '和'Progress '而不是'Dispatcher.Invoke'。 –