Dispatcher.Invoke循环冻结UI

ide*_*lix 0 wpf textbox dispatcher backgroundworker

我有日志拖尾应用程序,它在不定式循环中执行后台工作程序线程并检查某些TXT文件中的最新条目.一旦找到新条目,我使用Dispatcher.Invoke更新屏幕上的TextBox,并将最新条目添加到文本文件中.

问题是,如果源文本文件每毫秒不断更新,用户界面就会冻结,因为Dispatcher.Invoke经常更新文本框.

不知道是否有任何解决方法.我可以从文本文件中获取更大的块但不想破坏日志拖尾的实时体验,也不想延迟通过UI线程将行写入TextBox,因为这会使我的应用程序与实际数据不同步源文本文件.

这是后台工作者的DoWork方法

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
  reader = new StreamReader(new FileStream(file.File, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
  lastMaxOffset = reader.BaseStream.Length;

  while (true)
  {
    if (worker.CancellationPending)
    {
      e.Cancel = true;
      break;
    }                

    if (reader.BaseStream.Length == lastMaxOffset)
      continue;

    reader.BaseStream.Seek(lastMaxOffset, SeekOrigin.Begin);

    string line = "";
    while ((line = reader.ReadLine()) != null)

    this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
    {
      textLog.Text += "\r" + line;
      scrollViewer.ScrollToBottom();
    })); 

    lastMaxOffset = reader.BaseStream.Position;        
  }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,UI线程只是简单地将文本附加到TextBox,并且当它经常发生接口冻结时

Cod*_*lla 7

使用Dispatcher.Invoke是一个同步调用,因此它实际上与在UI线程上执行此操作相同,因为Invoke在UI执行所请求的任务之前调用阻塞.如果你在短时间内做得太多,那么你就有效地阻止了UI线程.

相反,您应该使用Dispatcher.BeginInvokeUI线程的工作队列,但不阻止.这将稍微好一些,就好像你在短时间内做得太多,你仍然充斥着UI线程的工作,这将花费大量时间来完成这项工作.

相反,最好的方法是将这些更改排队到UI线程,然后当队列达到定义的限制(即100个新的文本行)或超过特定的时间量(比如200ms)时,然后调用Dispatcher.BeginInvokesend对UI的这些更改.这将为您提供最佳的UI响应能力.