5

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

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

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

UI 线程将有一个计时器,该计时器触发一个设置间隔,以使用队列 2 中的新数据更新 UI。我想要做的是确定 UI 更新自身的速度以测量它在为了节流,例如增加/减少关于多久更新一次 UI 的计时器间隔

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

顺便说一句,这是一个好的设计还是可以改进?还有其他我可以考虑的策略吗?

4

2 回答 2

4

有 2 个线程……主 UI 线程……和渲染线程……你可能需要同时查看这两个线程以确定应用程序的响应能力……并决定如何节流。

测量你的帧率

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

使用计时器安排工作

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

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

在 UI 线程上执行委托

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

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

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

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

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

然后根据你的发现,你可以调整下一个工作单元。

监控各种系统性能计数器

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

Dispatcher 的一些背景知识

于 2012-08-16T19:07:22.390 回答
2

我会简单地删除 QueueManager 和 Queue2。

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

编辑

Dispatcher Timer 可以以特定的优先级启动。选择一个较低的值,例如 Background 或 ContextIdle,您将永远不会超载 GUI。
然后只需确保计时器不会咬掉超过它可以咀嚼的东西。一次只有 1 个(或几个)项目。调整这部分,以便 GUI 将尽可能多地处理,但仅此而已。运行时调整是免费的,因为您与调度程序相关联。

并且(仅在需要时)您可以使用 Queue1 = new BlockingCollection<MyItemType>(MaxItems) 该队列以防止溢出。

于 2012-08-16T18:59:08.323 回答