如何检测无响应的UI更新?

时间:2012-08-16 18:46:11

标签: c# .net wpf multithreading

假设我有一个WPF应用程序,我有以下架构:

[工作者主题] - > [队列1] - > [队列管理器线程] - > [队列2] - > [UI主题]

工作人员正在侦听来自某些服务的数据并在不确定的时间(可能是每秒多次或每几秒几次)接收该数据,然后将其排队到队列1,然后排队队列管理器,具体取决于“ UI线程的健康状况可能决定加快/减少数据项排队到队列2的速率,UI线程使用它来更新UI,可能会丢弃一些项目,以免压倒UI中的UI线程事件它接收太多消息(例如,如果消息之间的差异比UI更新自身的最后一个数据项至少早5秒,它可能会决定它将检查每个数据消息的时间戳并仅将其排入队列2 )

UI线程将有一个计时器,用于触发set-interval以使用来自Queue 2的新数据更新UI。我希望能够做的是确定UI更新自身以测量它的速度“响应性“为了节制,例如增加/减少关于更新UI的频率的计时器间隔

假设我的UI有很多控件(网格,图表等)都绑定到我的UI shell中队列2上数据的不同过滤/分组子集,并且UI在更新这些控件时开始变得无响应并在更新之间冻结,如何我可以从代码中检测到这一点,以便知道如何/何时增加/减少UI更新的间隔?基本上,我如何衡量在绑定到数据的所有控件上重新绑定整个UI所需的时间?

BTW这是一个很好的设计还是可以改进?我还有其他可以考虑的策略吗?

2 个答案:

答案 0 :(得分:4)

有2个主题...主要的UI线程......和渲染线程....您可能需要同时查看两者以确定应用程序的响应性......并决定如何限制。 / p>

测量你的FrameRate

您可以观看ETW事件,或者处理CompositionTarget.Rendering并自己计算帧....这是监视渲染线程...如果那是丢帧,那么它可以告诉您系统负担过重......你可以相应地限制你的背景工作。

使用计时器安排工作

您可以使用@HenkHolterman提到的DispatcherTimer来监视UI线程上的负载,方法是让它以低于Normal的优先级运行,例如:背景....当在UI线程上处理/处理计时器事件时......然后你可以释放/告诉你的后台工作人员做下一项工作。

但是要小心......如果您的计时器间隔太小......并且您的系统过载......那么您可能会收到计时器消息的累积......所以当您的事件处理程序被调用时......你可能会做太多的工作(除非你保留上次调用它的记录)。

在UI线程

上执行委托

或者你可以让一个委托在特定优先级的UI线程上运行(例如通常是背景)。

如果您使用Dispatcher.BeginInvoke ....那么工作将被放入队列,并将在所有优先级较高的工作完成后执行。 (异步)。

如果您使用Dispatcher.Invoke ...那么您的线程将阻塞,直到您调用的Dispatcher中的所有更高优先级的工作完成,然后您的委托将执行。

(您的后台工作人员在完成其工作单元后会执行此操作,然后委托人会告诉您的后台工作人员执行下一个工作单元。)

这可能比DispatchTimer更好,因为你可以消除一些延迟...即。使用计时器的延迟取决于计时器间隔。

然后根据您找到的内容,您可以调整下一个工作单元。

监控各种系统性能计数器

如果你真的想要变得复杂,那么你可以从应用程序内部监控各种性能计数器,例如使用内存,GC集合等......并动态调整你的工作量。

Dispatcher

的一些背景知识

答案 1 :(得分:2)

我只需删除QueueManager和Queue2。

您需要的只是一个WPF Dispatcher Timer(而不是Timers.Timer),它每隔几毫秒检查一次输入。您可以静态配置超时 然后使Queue1成为一个阻塞队列来限制上游的任何内容。

修改

Dispatcher Timer可以使用certain priority启动。选择一个低值,如Background或ContextIdle,你永远不会重载GUI 然后确保定时器不会咬得太多而不能咀嚼。一次只能有一个(或几个)项目。调整此部分,以便GUI尽可能多地处理,但不能再处理。运行时调整是免费的,因为你被绑定到调度程序。

并且(仅在需要时)您可以使用Queue1 = new BlockingCollection<MyItemType>(MaxItems),以便此队列不会溢出。