【问题标题】:Determine what is blocking UI thread确定是什么阻塞了 UI 线程
【发布时间】:2011-04-15 02:28:02
【问题描述】:

我正在开发一个相当大的 .NET WPF 实时应用程序。该应用程序运行良好且符合预期,除了一个大问题 - UI 更新缓慢。

这个应用程序是高度事件驱动的,有各种各样的事件引发 - 通过这些事件更新 UI。

其中一个或多个事件阻止 UI 立即显示。完成所有工作后,UI 会显示预期的结果。

有没有办法确定哪个事件处理程序导致了瓶颈?

【问题讨论】:

    标签: .net wpf multithreading user-interface block


    【解决方案1】:

    您可以使用代码分析器吗?这是他们擅长的事情。如果答案是否定的,我建议购买一个。

    除了使用分析器。您可以通过在您怀疑的代码块的开头和结尾放置时序语句来进行“穷人”分析。您甚至可以使用断点并使用挂钟对其进行计时。单击某些内容时是否会出现此问题?如果是这样,从那里开始。没有用户交互,这是一个反复出现的问题吗?然后从计时器开始。

    至于实际解决问题......除非违规处理程序正在做一些可以提高效率的事情,否则请考虑采用多线程方法。 .NET 4.0 的新任务库在这方面确实令人惊叹。

    【讨论】:

    • 嗨 Colithium。感谢您的回复。该应用程序是多线程的 - 在我的 Linq 查询和循环中使用并行。此外,我没有使用 Thread 对象,而是使用 Task.Factory.StartNew 进行多线程处理。该应用程序不包含计时器,也不包含任何后台工作程序。我有一个主要的非阻塞任务(线程),它只包含业务逻辑,即根本没有 UI 交互。此任务中的逻辑采取某些操作并引发与这些操作关联的事件。我正在使用 VS2010 - 它有一个分析器。不知道如何使用它以及如何评估结果。
    【解决方案2】:

    作为一阶近似,我发现中断调试器(使用 IDE 中的暂停按钮)并查看堆栈很有用。多做几次,看看有没有规律。你总是在同一个功能?您是否为响应事件而做一些昂贵的事情?您是否收到了更多您期望的事件?它技术含量低,但非常有效。

    【讨论】:

      【解决方案3】:

      我完全支持 colithium 使用分析器的建议。

      此外,如果阻塞时间超过一秒,您可以点击 Visual Studio 中的“暂停”按钮。在工具栏中,有一个下拉列表,您可以在其中选择“主线程”。然后跳转到当前阻塞 UI 的方法。

      【讨论】:

      • 谢谢海因策!试过你的建议,效果很好。非常有用且简单易记的技巧。信不信由你,这是导致问题的应用程序日志。使用 log4net appender 更新 RichTextBox 日志 UI。
      • 您说:“在工具栏中,有一个下拉列表,您可以在其中选择主线程”。你的意思是你可以看到你点击暂停时主线程在哪里?还是您的意思是您可以找出阻塞主线程的原因?我知道有一个调试窗口有一个线程列表。这就是工具栏中的下拉菜单的意思吗?我注意到,当我暂停一个长时间的操作时,我经常会在 UI 线程上的代码中碰到一个不需要很长时间的特定区域。所以 UI 线程必须在某些东西(信号量或其他东西)上被阻止有没有办法查看如果它不在我的代码中怎么办?
      【解决方案4】:
        public class UIBlockDetector
      {
          static Timer _timer;
          public UIBlockDetector(int  maxFreezeTimeInMilliseconds = 200)
          {
              var sw = new Stopwatch();
      
              new DispatcherTimer(TimeSpan.FromMilliseconds(10), DispatcherPriority.Send, (sender, args) =>
              {
                  lock (sw)
                  {
                      sw.Restart();
                  }
      
              }, Application.Current.Dispatcher);
      
              _timer = new Timer(state =>
              {
                  lock (sw)
                  {
                      if (sw.ElapsedMilliseconds > maxFreezeTimeInMilliseconds)
                      {
                          // Debugger.Break() or set breakpoint here;
                          // Goto Visual Studio --> Debug --> Windows --> Theads 
                          // and checkup where the MainThread is.
                      }
                  }
      
              }, null, TimeSpan.FromMilliseconds(0), TimeSpan.FromMilliseconds(10));
      
          }
      
      }
      

      在 MainWindow 构造函数中新建这个类。当断点命中时,您可以转到 Visual Studio --> Debug --> Windows --> Threads 并检查哪些操作阻止了您的 UI-Thread!

      【讨论】:

      • 在您选择的地方新建类 + 在“if (sw.ElapsedMilliseconds > maxFreezeTimeInMilliseconds)”中设置断点。这个类只是主线程的看门狗逻辑。
      • 你在这里用什么定时器?
      • 哇。这真的很棒。那里不错。我会把它放在手边。
      • 我保存过的最有用的 sn-ps 之一!谢谢@Andreas
      • @Andreas 是 10 毫秒会导致 DispatcherTimer 的主线程出现任何瓶颈吗?您选择该值的任何原因?
      猜你喜欢
      • 2013-09-08
      • 2013-04-02
      • 2016-01-11
      • 1970-01-01
      • 2021-08-29
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      相关资源
      最近更新 更多