确定阻塞UI线程的原因

13

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

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

其中一些事件阻塞了UI无法立即显示。当所有工作完成后,UI才显示预期结果。

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

4个回答

26
  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线程!


你在这里使用的是哪个计时器? - frostymarvelous
哇,这真的非常非常好。做得好。我会记下来的。 - frostymarvelous
@Andreas,10毫秒会对DispatcherTimer的主线程造成任何瓶颈吗?您选择这个值的原因是什么? - mattyb
这太聪明了,对我帮助很大。谢谢! - xxdefaultxx
这似乎正是我所需要的,但我不会点赞,因为我不得不花时间找出可以轻松包含在代码片段中的使用语句。 - Geoff Johnson
显示剩余2条评论

12
我完全支持colithium使用分析器的建议。
此外,如果阻止耗时超过一秒钟,则可以尝试在Visual Studio中点击“暂停”按钮。在工具栏中,有一个下拉列表,可以选择“主线程”。然后会跳转到当前阻塞UI的方法。

1
谢谢Heinze!尝试了您的建议,效果非常好。非常有用和简单的技巧可以记住。信不信由你,是应用程序日志导致了问题。使用log4net appender更新RichTextBox日志UI。 - c0D3l0g1c
你说:“在工具栏中,有一个下拉列表,你可以选择主线程”。你的意思是你可以看到当你暂停时主线程所在的位置吗?还是你的意思是你可以找出是什么阻塞了主线程?我知道有一个调试窗口,其中有一个线程列表。这就是你所说的工具栏中的下拉菜单吗?我注意到当我暂停一个长时间的操作时,我经常会在 UI 线程上的特定代码区域上停留,而这不应该花费很长时间。因此,UI 线程必须被某些东西(信号量或其他)阻塞了,有没有办法看到它是否不在我的代码中? - ILIA BROUDNO

6

您是否拥有代码分析器?这是它们擅长的类型。如果没有,建议获取一个。

除了使用分析器外,您可以通过在怀疑的代码块的开头和结尾放置计时语句来进行“穷人版”分析。甚至可以使用断点并用挂钟计时。如果问题发生在单击某个内容时,请从那里开始。如果是无用户交互的重复问题,请从计时器开始。

至于实际解决问题...除非有问题的处理程序正在执行可以更高效的操作,否则请考虑采用多线程方法。对于.NET 4.0来说,新的任务库真的非常出色。


嗨Colithium。谢谢你的回应。该应用程序是多线程的 - 在我的Linq查询和循环中利用了并行性。此外,我没有使用Thread对象,而是使用Task.Factory.StartNew进行多线程处理。该应用程序不包含任何计时器或后台工作者。我有一个主要的非阻塞任务(线程),它仅包含业务逻辑,即根本没有任何UI交互。此任务中的逻辑执行某些操作并引发与这些操作相关联的事件。我正在使用VS2010 - 它具有分析器。不知道如何使用它以及如何评估结果。 - c0D3l0g1c

0
作为第一阶近似,我发现在调试器中断点(使用IDE中的暂停按钮)很有用,并查看堆栈。多做几次,你就可以看到是否存在某种模式。你总是在同一个函数中吗?你是否在响应事件时执行了一些昂贵的操作?你是否收到了比预期更多的事件?这很低技术含量,但非常有效。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接