为什么System.Windows.Threading.DispatcherTimer没有触发事件?

3
我有一个VB.Net和WPF应用程序。该应用程序的一部分使用“图像生成器”计算图像。另一部分使用“定时器”显示直方图。当使用生成器每秒创建约2帧时,CPU使用率为零,图像生成良好,并且直方图将在1秒钟的定时器中创建。所有看起来都很完美。
如果我使用自己的“图像生成器”计算每秒约20帧的图像,CPU占用率会上升到20%,但是定时器永远不会触发事件。GUI中的图像显示20FPS。如果我停止图像生成器,则定时器立即触发它的事件。
我不知道从哪里开始搜索。
Me.TimerCalc = New System.Windows.Threading.DispatcherTimer
Me.TimerCalc.Interval = TimeSpan.FromMilliseconds(1000)
Me.TimerCalc.Start

Private Sub TimerCalc_Tick(sender As Object, e As System.EventArgs) Handles TimerCalc.Tick
'Does not fire if other process is running by using more CPU
End Sub

如果我将帧速率降低到约10FPS,有时DispatcherTimer会触发其事件,有时不会。如果我增加帧速率,DispatcherTimer将会触发更少的事件。
编辑:在此处下载演示项目:VisualStudio 2010 Dispatcher Test with Windows Form Timer 编辑:演示视频已上传至此处:视频中立方体不再旋转

了解消息循环在Windows GUI编程中是基础。只有当您的代码处于空闲状态时,Tick才能触发。 - Hans Passant
4个回答

3

在我的设备上,我无法重复这个问题。对我来说一切都正常。

不过,我想知道你在TimerCalc_Tick函数中做了什么。你在那里进行的任何操作都会阻塞Dispatcher。事件将不会再次触发,直到完成该事件为止。所以我确实可以看到那会引起一些问题。

经过进一步查看代码、观看视频并在我的机器上测试后,我认为当你达到更高速率时,你只是让Dispatcher处于饥饿状态。我不确定为什么矩形对你完全停止旋转(对我来说没有这种情况),但如果你的处理器/显卡低于我的配置,我可以理解这种差异。当我像你在视频中那样将滑块全部向左移动时,我的处理器(所有四个核心)被占用了约40%。低端机器会比这更差,并且可能会饿死其他线程。

如果你采用上面发布的项目,并只进行单个更改,将:

Public Sub New()
    Me.TimerRefresh = New System.Windows.Forms.Timer
    Me.TimerRefresh.Interval = Me.Intervall
    Me.TimerRefresh.Enabled = True
End Sub

将其转换为:

Public Sub New()
    Me.TimerRefresh = New System.Windows.Forms.Timer
    Me.TimerRefresh.Interval = Me.Intervall
    Me.TimerRefresh.Enabled = False
End Sub

如果您不实际启动处理所有进程的计时器,然后运行应用程序并将滑块全部向左移动,矩形仍会停止吗?如果不是,则我有信心您只是在饿死调度程序。如果有...那么我仍然感到困惑。


非常感谢您的回答,Tim。Private Sub TimerCalc_Tick(sender As Object, e As System.EventArgs) Handles TimerCalc.Tick Debug.WriteLine("TimerCalc_Tick: " & DateTime.Now.Ticks) End Sub - goldengel
你只是这样做吗?嗯,肯定还有其他问题。我可以将完全相同的代码放入示例应用程序中,即使每秒执行100次也能正常工作(我不确定它实际上是否达到了这个速度,但我将其提高到了那个速度以查看会发生什么)。您是否尝试过在示例项目中简化代码以隔离问题? - Tim
我已经在顶部上传了一个演示项目。 - goldengel
嗯,我仍然无法复制该问题。我采用了您上传的代码(我注意到已更改为使用表单计时器),代码大致按照我的预期触发。切换回DispatcherTimer也一样。它不能始终跟上(当我们接近100fps时,我会这么想),但它从未停止引发事件。 - Tim
在观看了视频后,我给我的回答添加了一些思考。 - Tim
显示剩余2条评论

0
如果您想要短且稳定的时间间隔,请使用多媒体计时器(在Code Project网站上找到.NET包装器)。
如果您需要在UI线程上进行工作,请在多媒体计时器事件中调用调度程序(而不是调度程序计时器)。将调度程序优先级设置为输入,以确保UI不会阻塞它。请确保操作所需时间远小于时间间隔。
建议使用5毫秒及以上的时间间隔。

0

当更新图像时,调度程序会被阻塞。通过使用新线程来解除阻塞。

Private Sub cImageGenerator_NewImageAvailable() Handles cImageGenerator.NewImageAvailable
        If _IsReady = False Then Exit Sub
        _IsReady = False
        ' ################
        ' # Thread - Christian Zech
        ' #################
        Dim trd As New System.Threading.Thread(Sub()
                                                   ' ##########################
                                                   ' # Clone
                                                   ' ##########################
                                                   Dim bm As System.Drawing.Bitmap = Me.cImageGenerator.CreateBitmap '.Clone
                                                   Dim imgSrc As ImageSource = BitmapToWpfBitmapSource(bm)
                                                   imgSrc.Freeze() 'needed because otherwise we will get the error: The calling thread cannot access this object because a different thread owns it.
                                                   Me.Dispatcher.Invoke(New Action(Of ImageSource)(AddressOf UpdateImage), imgSrc)
                                               End Sub)
        trd.SetApartmentState(System.Threading.ApartmentState.STA) ''ToDo: MTA probieren
        trd.Name = "cImageGenerator_NewImageAvailable"
        trd.Start()
    End Sub

    Private Sub UpdateImage(ByVal img As ImageSource)
        Me.img1.Source = img
        _IsReady = True
    End Sub

0

很遗憾,DispatcherTimer 无法每50毫秒(每秒20次)触发一次。你的 CPU 可能只显示20% 的使用率,因为你可能有多个核心,但 WPF GUI 线程只在一个核心上运行。

要找出 DispatcherTimer 能够多快地触发,请将其以1毫秒的DispatcherTimer.Interval和默认优先级DispatcherPriority.Background运行。Tick 被实际调用得较慢。在我的 PC 上,Tick 每3..300毫秒运行一次,即使 WPF 没有其他事情可做。

你可以将优先级提高到 DispatcherPriority.Input ,那么 Tick 每4...125毫秒运行一次。你可以使用更高的优先级,但渲染会变得不稳定。

即使你选择了200毫秒的间隔并计数它们,你也会发现一些 Tick 随着时间的推移而丢失。你可以通过测量每个 Tick 被延迟了多少毫秒并相应地缩短间隔来改善它。

有关详情,请参阅我在 CodeProject 上的文章: Improving the WPF DispatcherTimer Precision


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