我有一个WPF应用程序,需要对许多小任务进行一些处理。 这些小任务都是同时生成的,并添加到Dispatcher Queue中,优先级为Normal。同时显示忙碌指示符。结果是繁忙的指标实际上已经冻结,尽管工作被分解为任务。
我尝试将这些任务的优先级更改为后台以查看是否已修复它,但忙碌指示仍然冻结。
我订阅了Dispatcher.Hooks.OperationStarted
事件,看看在我的任务处理过程中是否发生了任何渲染作业,但他们没有。
任何想法发生了什么?
一些技术细节:
这些任务实际上只是来自Observable
序列的消息,它们通过调用ReactiveUI的ObserveOn(RxApp.MainThreadScheduler)
“排队”到调度程序中,该ObserveOn(DispatcherScheduler)
应该等同于IObservable<TaskMessage> incomingTasks;
incomingTasks.ObserveOn(RxApp.MainThreadScheduler).Subscribe(SomeMethodWhichDoesWork);
。每个任务的工作部分是通过ObserveOn调用订阅的代码,例如
{{1}}
在这个例子中,incomingTasks会连续产生3000多条消息,ObserveOn将每次调用SomeMethodWhichDoesWork推送到Dispatcher队列,以便稍后处理
答案 0 :(得分:5)
您看到忙碌指示器停止的原因是因为SomeMethodWhichDoesWork
花了太长时间。在它运行时,它会阻止在Dispatcher上发生任何其他工作。
为处理动画而生成的输入和渲染优先级操作低于正常,但优先于后台操作。但是,Dispatcher 上的操作不会因更高优先级操作的入队而中断。因此,渲染操作必须等待正在运行的操作,即使它是后台操作。
ObserveOn(DispatcherScheduler)
会将所有内容推送到普通优先级。更新版本的Rx在重载时允许您指定优先级。
有一点要强调的是,经常错过的是DispatcherScheduler一旦到达 NOT 就会将项目排队到Dispatcher上。
因此,如果您的3000个项目都相当接近,您将在Dispatcher上备份3000个正常优先级的操作,阻止相同或较低优先级的所有内容,直到完成为止 - 包括渲染操作。这几乎可以肯定你所看到的 - 这意味着你可能仍然会看到问题,即使你在后台线程上进行UI更新工作,取决于你的UI更新有多重。
除此之外,你应该检查你没有在UI线程上运行整个订阅 - 正如Lee所说。我通常编写我的代码,以便我在后台线程上Subscribe
而不是使用SubscribeOn,尽管这也很好。
无论你做什么,都要在后台线程上做尽可能多的工作。这一点已经在StackOverflow和其他地方完成了。以下是一些很好的资源:
如果您希望在面对大量小更新时保持UI响应,您可以:
值得稍微退一步看一下大局。
如果您将3000个项目连续分别转储到用户界面,那么该怎么做?他们最多将运行一台刷新率为100Hz的显示器,可能更低。我发现每秒10帧的帧速率对于大多数用途来说已经足够了。
不仅如此,人们认为一次不能处理超过5-9位的信息 - 所以你可能会找到更好的方法来聚合和显示信息,而不是一次更新这么多东西。例如,使用主/视图而不是一次在屏幕上显示所有内容等等。
另一种选择是查看UI更新导致的工作量。一些控件(我正在看你XamDataGrid)可以有非常冗长的度量/排列布局操作。你能简化你的动画吗?使用更简单的Visual树?想想看起来像圆圈点的流行繁忙的微调器 - 但实际上它只是改变它们的颜色。实现的效果相当便宜。值得对您的应用程序进行分析,以了解时间的进展情况。
我也会考虑前后的总体方法。如果您有理由确定要立即更新那么多项目,为什么不缓冲它们并以块的形式管理它们?这可能会一直有利于源 - 这可能是在服务器的某个地方?在任何情况下,Rx都有一些不错的运算符,比如Buffer
可以将单个项的流转换为更大的列表 - 并且它具有可以按时间缓冲和大小的重载。 / p>
答案 1 :(得分:0)
您是否尝试使用.SubscribeOn(TaskPoolScheduler.TaskPool)
订阅其他主题?
答案 2 :(得分:0)
@Pedro Pombeiro有正确的答案。 您在UI上看到冻结的原因是您在Dispatcher上排队工作。这意味着工作将在UI线程上完成。您可以将Dispatcher视为一个消息泵,它不断地从每个队列中排出消息(您可以考虑每个优先级[SystemIdle,ApplicationIdle,ContextIdle,Background,Input,Loaded,Render,DataBind,Normal,Send) ])
将您的工作放在不同的优先级队列上,不会让它同时运行,只是异步运行。
要使用Rx在另一个线程上运行您的工作,请使用上面的SubscribeOn。请记住,然后使用ObserveOn将任何更新重新安排到Dispatcher上。