WPF 如何知道当前有多少个 DispatcherTime 在运行

时间:2021-05-24 01:16:51   收藏:0   阅读:0

在 WPF 调试中,对于 DispatcherTimer 定时器的执行,没有直观的调试方法。本文来告诉大家如何在 WPF 中调试当前主线程有多少个 DispatcherTimer 在运行

在 WPF 中,如果有 DispatcherTimer 定时器在执行,将会影响到主线程的执行,将会让主线程诡异忙碌。或者有 DispatcherTimer 定时器忘记订阅 Tick 同时也忘记设置时间,将会不断消耗主线程资源。在遇到没有任何的交互时,此时出现的主线程卡的问题,可以优先尝试了解是否 DispatcherTime 定时器的问题

如以下代码,在界面创建一个按钮,点击按钮时将会创建和运行 DispatcherTime 定时器

  <Grid>
    <Button Margin="10,10,10,10" HorizontalAlignment="Left" VerticalAlignment="Top" Content="按钮" Click="Button_OnClick" />
  </Grid>

Button_OnClick 加上创建和运行 DispatcherTimer 定时器的代码

        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            var dispatcherTimer = new DispatcherTimer()
            {
                Interval = TimeSpan.FromSeconds(1)
            };
            dispatcherTimer.Start();
        }

假定需要调试在进入 Button_OnClick 方法,当前主线程有多少 DispatcherTimer 定时器在运行。可以在此方法上添加断点,如下图

技术图片

进入 VisualStudio 的监视界面,输入 System.Windows.Threading.Dispatcher.CurrentDispatcher._timers 即可了解当前主线程有多少个 DispatcherTime 定时器在运行。根据 Dispatcher.CurrentDispatcher 的定义,此 CurrentDispatcher 是 ThreadStatic 线程静态,因此以上调试断点必须落在主线程执行的代码

技术图片

如进一步了解当前的 DispatcherTimer 定时器是由哪个业务模块定义的,可以通过 Tick 委托找到对应的业务模块,如下图

技术图片

通过 Tick 委托可以了解到是哪个类的哪个方法,通过静态代码可以找到业务

如果只是期望调试 DispatcherTimer 定时器是由哪个业务模块启动创建的,此时可以添加函数断点,添加函数断点步骤相对复杂

在开始之前,需要加载 WindowsBase.dll 的符号,同时我也推荐使用 dotnet core 或 .NET 5 版本的 WPF 框架进行调试,因此此框架可以有源代码支持。加载 WindowsBase.dll 的符号之前,还请到开源的 WPF 项目,将代码拉到本地

加载 WindowsBase.dll 的符号方法是在 调试->窗口->模块 打开模块窗口,右击 WindowsBase.dll 加载符号

技术图片

加载符号时需要一点网络,基本上都能加载成功。为什么需要加载 WindowsBase.dll 的符号?原因是 System.Windows.Threading.DispatcherTimer 定义在 WindowsBase 程序集

接下来在断点窗口里面,点击新建函数断点

技术图片

输入调试的函数,进行断点,如断点放在 System.Windows.Threading.DispatcherTimer.Start 函数里面,当然,这只是一个例子

技术图片

这样在触发 DispatcherTimer.Start 函数将会进入断点

断点进入之后,默认都会提示是否选择源代码,这就是上文推荐使用开源版本的 WPF 框架的原因,因为咱可以找到此文件进行加载,这样就能实现源代码级的调试。如果发现源代码有一些不匹配,也莫慌,将自己的代码仓库切换到对应的分支或 tag 即可,或者去掉勾选源代码必须匹配选项

技术图片

通过调用堆栈即可了解到当前是哪个模块调用了 DispatcherTimer.Start 函数

技术图片

以上步骤比较多,还需要大家玩一下才能了解

其实 DispatcherTimer.Start 函数将会在框架内部消息里面不断调用,因此断点不建议落在 Start 函数,按照咱的调试需求,其实更多的是在构造函数,可以在函数断点里面输入 System.Windows.Threading.DispatcherTimer.DispatcherTimer 调试是哪个业务创建 DispatcherTimer 对象

本文所有代码放在 githubgitee 欢迎小伙伴访问

更多代码调试请看 dotnet 代码调试方法

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!