C#silverlight中的DispatcherTimer和UI刷新限制

Pau*_*ulG 8 c# silverlight multithreading dispatchertimer visiblox

我再次为一个对你们所有人都很简单的问题道歉.我对Silverlight幕后的内容知之甚少.

我有一个图表应用程序(Visiblox),我用它作为滚动范围每20ms更新一次,添加和删除一个点.在伪代码中:

List<Point> datapoints= new List<Point>();
Series series = new Series(datapoints);
void timer_tick(){
  datapoints.Add(new Point);
  datapoints.RemoveAt(0);
  // no need to refresh chart, it does refresh automatically
}
Run Code Online (Sandbox Code Playgroud)

在此图表工具中运行6系列时,它开始显示有点迟缓.将刻度线更改为10毫秒没有任何区别,图表以相同的速度更新,因此似乎20ms是速度限制(UI或图表?).

我试过CompositionTarget.Rendering并得到了相同的结果:在20ms以下,速度没有差别.

然后我意外地启用了两个并且速度加倍.所以我测试了多个线程(2,3,4),速度加倍,三倍和四倍.这还没有锁,因为我甚至不知道生成锁定需要什么进程,但没有数据损坏和内存泄漏.

我的问题是为什么20ms的低速图表不能在10ms运行,但在多线程时速度非常快?UI刷新过程是否运行得更快?图表计算加倍了吗?或者单个DispatcherTimer的执行速度是否有限制?

谢谢!


编辑:我有嵌入式编码的背景,所以当我想到线程和时序时,我立即想到在硬件中切换一个引脚并连接一个范围来测量进程长度.我是C#中的线程新手,没有用于连接范围的引脚.有没有办法以图形方式查看线程时序?

Jos*_*osh 7

DispatcherTimer在UI线程上触发其Tick事件,被认为是低分辨率或低精度的计时器,因为它的Interval实际上意味着"自上次打勾以来不早于x".如果UI线程忙于执行任何操作(处理输入,刷新图表等),那么它将延迟计时器的事件.此外,在UI线程上以非常低的间隔勾选一堆DispatcherTimer也会降低应用程序的响应速度,因为在引发Tick事件时,应用程序无法响应输入.

正如您所指出的,为了频繁处理数据,您应该转到后台线程.但有一些警告.您目前没有观察到腐败或其他错误的事实可能纯属巧合.如果在前台线程尝试从中读取列表的同时在后台线程上修改列表,则最终会崩溃(如果幸运的话)或者看到损坏的数据.

在您的示例中,您有一条评论说"无需刷新图表,它会自动刷新".这让我想知道图表是如何知道你改变了datapoints收藏品的?List<T>修改时不会引发事件.如果您使用的是ObservableCollection<T>我会指出,每次删除/添加一个点时,您可能会刷新图表,这可能会减慢速度.

但是,如果你实际上正在使用List<T>那么必须有其他东西(也许是另一个计时器?)刷新图表.也许图表控件本身有内置的自动刷新机制?

无论如何,这个问题有点棘手,但并不是全新的.有一些方法可以在后台线程上维护集合并从UI线程绑定到它.但是,UI刷新的速度越快,您就越有可能等待后台线程释放锁定.

最小化这种方法的一种方法是使用a LinkedList<T>而不是List<T>.添加到LinkedList的末尾是O(1),因此删除项目.一个List<T>需要,当你从一开始就删除项目的一切由一个向下移位.通过使用LinkedList,您可以在后台线程中锁定它,并且您将最小化您持有锁的时间.在UI线程上,您还需要获取相同的锁,并将列表复制到数组或在锁定时刷新图表.

另一种可能的解决方案是在后台线程上缓冲点的"块",并使用Dispatcher.BeginInvoke将其批量发布到UI线程,然后您可以安全地更新集合.


wjb*_*eau 4

我认为这里的关键是要认识到 Silverlight 默认情况下以 60fps 的最大帧速率渲染(可通过 MaxFrameRate 属性进行自定义)。这意味着 DispatcherTimer 每秒最多触发 60 次。此外,所有渲染工作也发生在 UI 线程上,因此 DispatcherTimer 最多以绘图发生的速率触发,如上一张海报所指出的。

通过添加三个计时器所做的结果只是在每个事件循环中触发“添加数据”方法 3 次而不是一次,因此看起来图表运行得更快,但实际上帧速率大致为相同。您可以使用单个 DispatcherTimer 获得相同的效果,只需在每个 Tick 上添加 3 倍的数据即可。您可以通过挂钩 CompositionTarget.Rendering 事件并并行计算帧速率来验证这一点。

之前提出的 ObservableCollection 点是一个很好的点,但在Visiblox中,有一点魔法可以尝试减轻其影响,因此如果您以非常快的速度添加数据,图表更新将以渲染循环和不必要的重新渲染将被删除。

另外,关于与 IDataSeries 的 ObservableCollection 实现相关联的观点,您可以完全自由地自己实现 IDataSeries 接口,例如通过使用简单的列表来支持它。请注意,显然如果您这样做,当数据发生变化时图表将不再自动更新。您可以通过调用 Chart.Invalidate() 或更改手动设置的轴范围来强制图表更新。