0

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

问题在于,如果源文本文件每毫秒不断更新,那么用户界面就会冻结,因为 Dispatcher.Invoke 如此频繁地更新 Textbox。

想知道是否有任何解决方法。我可以从文本文件中获得更大的块,但不想破坏日志跟踪实时体验,也不想延迟通过 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;        
  }
}

如您所见,UI 线程只是简单地将文本附加到 TextBox,而当它碰巧经常界面冻结时

4

4 回答 4

10

UsingDispatcher.Invoke是一个同步调用,因此它实际上与在 UI 线程上执行此操作相同,因为调用Invoke阻塞直到 UI 执行请求的任务。如果你在短时间内做太多事情,你实际上是在阻塞 UI 线程。

相反,您应该使用Dispatcher.BeginInvoke哪些排队等待 UI 线程执行的工作,但不会阻塞。这会稍微好一点,就好像你在短时间内做的太多了,你仍然在 UI 线程中充斥着工作来做它会花费很多时间来做这项工作。

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

于 2012-06-14T17:40:24.047 回答
1

我同意 CodingGorilla 的回答。

使用 Dispatcher.Invoke 将导致同步调用。

这将与直接在 UI 线程上进行调用具有相同的结果。

调用将阻塞,直到 UI 执行请求的任务,如果这种情况快速连续发生,您的 UI 线程将阻塞,因此您将不会获得任何更新。

尝试用 Dispatcher.BeginInvoke 替换您的Dispatcher.Invioke 看看这是否能解决您的问题。

// Load in background
this.Dispatcher.BeginInvoke(new Action(() =>
{
    textLog.Text += "\r" + line;
    scrollViewer.ScrollToBottom();

}));

同样如建议的那样,您可能希望循环休眠 1 毫秒;使用Thread.Sleep(1);每隔一段时间,这样你就不会最终占用cpu。

于 2013-12-18T00:30:45.137 回答
1

通过调用Dispatcher.Invoke您读取的每一行,您实际上是在使每一行将数据推送回 UI 线程,并等待它完成。

这可能会使整个例程比直接使用 UI 线程,因为您增加了开销,但不会将大部分工作拉入后台线程。

为了加快速度,您需要做一些事情来缓冲数据,并以更大的块发送数据。在这种情况下,由于您正在寻找新的日志条目,我建议您一次读取文件末尾的所有日志条目,并将整个块编组回您的 UI(而不是逐行执行) )。

于 2012-06-14T17:37:43.097 回答
1

抱歉,但在无限循环中读取文件既错误又……嗯……愚蠢。有像FileSystemWatcher这样定义明确的类,没有必要仅仅因为你想要文件的实时更新而将 100% 的负载放在你的 CPU 核心上。

我认为您对编程很陌生-好吧,我们都会犯错误并且必须学习,接受以下建议:

  • 无休止的循环是自动编程错误
  • 如果确实需要循环,建议在每次迭代之间让相应的线程休眠
于 2013-01-13T12:54:59.340 回答