5

我有一个使用 DispatcherTimer 更新时钟滴答的 WPF 应用程序。

但是,在我的应用程序运行大约 6 小时后,时钟指针角度不再变化。我已经验证 DispatcherTimer 仍在使用 Debug 触发并且角度值仍在更新,但是屏幕渲染并未反映更改。

我还使用 WPFPerf 工具 Visual Profiler 验证了 Unlabeled Time、Tick(时间管理器)和 AnimatedRenderMessageHandler(媒体内容)都在逐渐增长,直到它们消耗了近 80% 的 CPU,但内存运行稳定。

hHandRT.Angle 是对 RotateTransform 的引用

hHandRT = new RotateTransform(_hAngle);

此代码在大约 5 小时的直线运行中完美运行,但之后它会延迟并且角度变化不会呈现到屏幕上。有关如何解决此问题的任何建议或您可能知道的任何可能的解决方案。

.NET 3.5、Windows Vista SP1 或 Windows XP SP3(都显示相同的行为)

编辑:添加时钟滴答功能

//In Constructor
...
_dt = new DispatcherTimer();
_dt.Interval = new TimeSpan(0, 0, 1);
_dt.Tick += new EventHandler(Clock_Tick);
...

 private void Clock_Tick(object sender, EventArgs e)
        {

            DateTime startTime = DateTime.UtcNow;
            TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
            _now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
            int hoursInMinutes = _now.Hour * 60 + _now.Minute;
            int minutesInSeconds = _now.Minute * 60 + _now.Second;
            _hAngle = (double)hoursInMinutes * 360 / 720;
            _mAngle = (double)minutesInSeconds * 360 / 3600;
            _sAngle = (double)_now.Second * 360 / 60;
            // Use _sAngle to showcase more movement during Testing.
            //hHandRT.Angle = _sAngle;
            hHandRT.Angle = _hAngle;
            mHandRT.Angle = _mAngle;
            sHandRT.Angle = _sAngle;

            //DSEffect
            // Add Shadows to Hands creating a UNIFORM light
            //hands.Effect = textDropShadow;
        }

由于时钟滴答声发生了太多事情,我目前正在尝试这种调整,看看它是否有帮助。太糟糕了,这个错误需要 5 个小时才能显现出来 :(

  //DateTime startTime = DateTime.UtcNow;
  //TimeZoneInfo tst = TimeZoneInfo.FindSystemTimeZoneById(_timeZoneId);
  //_now = TimeZoneInfo.ConvertTime(startTime, TimeZoneInfo.Utc, tst);
  _now = _now.AddSeconds(1);
4

3 回答 3

5

你说你每次都在创建一个 Clock 类的实例?请注意,.NET 中的计时器将自己植根以防止自己被垃圾收集。它们会一直开火,直到您自己停止它们,并且它们将使您的 Clock 对象保持活动状态,因为它们在计时器滴答事件中被引用。

我认为正在发生的事情是,您创建的每个时钟都会启动另一个计时器。起初,您每秒只触发 1 个事件,但随后您在另一个计时器上添加并每秒获得 2 个事件,并且它们继续以这种方式累积。最终,您会看到 Tick 处理程序和 AnimatedRenderMessageHandler 在 CPU 中上升,直到它们陷入困境并且无法更新您的屏幕。这也可以解释为什么增加计时器触发的频率会使您的症状更快出现。

修复应该很简单:当您使用完 Clock 对象后,只需停止或处置 DispatcherTimer。

于 2009-10-13T04:46:10.393 回答
1

您假设它是 DispatcherTimer 并完全专注于此。我个人很难相信它与计时器本身有关,而是认为它与您在计时器滴答声中所做的任何事情有关。你能告诉我们更多关于每次计时器滴答时发生的事情吗?

于 2009-10-12T18:11:45.923 回答
1
hHandRT.Angle = _hAngle;
mHandRT.Angle = _mAngle;
sHandRT.Angle = _sAngle;

我相信您必须再次查看上面的代码。

您正在为所有 3 个变换设置变换的角度属性,即使您不需要它们每秒都更改。每改变 60 次,分钟就会改变,每 3600 秒改变小时。但是,您至少可以减少每秒变化的小时数。

这里发生的情况是,每当您请求对 WPF 进行转换更改时,WPF 会将请求排队到优先调度队列,并且每一秒您都会推送更多更改以完成,然后它可以处理。这是您的 CPU 使用率不断增加而不是内存增加的唯一原因。

详细分析:

查看您的代码后,我觉得您的 DispatcherTimer_Tick 事件进行了太多计算,请记住,如果让您的 CPU 更忙于在调度程序中执行自定义任务,调度程序线程已经有很多事情要做,例如管理事件路由、视觉更新等在每一个第二个事件中线程,它肯定会继续增加待处理任务的队列。

您可能认为这是一个小的乘法计算,但对于 Dispatcher 线程,在加载时区、转换时间值等方面可能会很昂贵。您应该分析并查看滴答执行时间。

您应该使用 System.Threading.Timer 对象,该对象将在另一个线程上运行,在每个刻度事件之后,当您完成所需的最终角度的计算后,您可以将它们传递给 Dispatcher 线程。

像,

Dispatcher.BeginInvoke((Action)delegate(){
   hHandRT.Angle = _hAngle;
   mHandRT.Angle = _mAngle;
   sHandRT.Angle = _sAngle;   
});

通过这样做,您将稍微减少调度程序线程的工作量。

于 2009-10-14T12:56:13.750 回答