处理许多小任务并保持UI响应 [英] Process lots of small tasks and keep the UI responsive

查看:112
本文介绍了处理许多小任务并保持UI响应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WPF应用程序,需要处理许多小任务。
这些小任务都同时生成,并以正常优先级添加到分派器队列中。同时,正在显示忙碌指示器。结果是尽管工作分解成多个任务,但忙碌指示器实际上仍然冻结。

I have a WPF application that needs to do some processing of many small tasks. These small tasks are all generated at the same time and added to the Dispatcher Queue with a priority of Normal. At the same time a busy indicator is being displayed. The result is that the busy indicator actually freezes despite the work being broken into tasks.

我尝试将这些任务的优先级更改为背景,以查看是否可以解决该问题,

I tried changing the priority of these tasks to be Background to see if that fixed it, but still the busy indicator froze.

我订阅了 Dispatcher.Hooks.OperationStarted 事件以查看是否有渲染工作是在我的任务正在处理时发生的,但没有。

I subscribed to the Dispatcher.Hooks.OperationStarted event to see if any render jobs occurred while my tasks were processing but they didn't.

任何想法都在发生什么事情?

Any ideas what is going on?

一些技术细节:
这些任务实际上只是来自 Observable 序列的消息,并且通过调用ReactiveUI的<$ c将它们排队到调度程序中$ c> ObserveOn(RxApp.MainThreadScheduler)应该等效于 ObserveOn(DispatcherScheduler)。这些任务中每个任务的工作部分都是通过ObserveOn调用订阅的代码,例如

Some technical details: The tasks are actually just messages coming from an Observable sequence, and they are "queued" into the dispatcher by a call to ReactiveUI's ObserveOn(RxApp.MainThreadScheduler) which should be equivalent to ObserveOn(DispatcherScheduler). The work portion of each of these tasks is the code that is subscribing through the ObserveOn call e.g.

IObservable<TaskMessage> incomingTasks;
incomingTasks.ObserveOn(RxApp.MainThreadScheduler).Subscribe(SomeMethodWhichDoesWork);

在此示例中,incomingTasks可能会连续短时生成3000多个消息,ObserveOn将每个调用推送到SomeMethodWhichDoes可以处理Dispatcher队列,以便稍后处理

in this example, incomingTasks would produce maybe 3000+ messages in short succession, the ObserveOn pushes each call to SomeMethodWhichDoesWork onto the Dispatcher queue so that it will be processed later

推荐答案

基本问题


您看到指标繁忙的原因是,您的 SomeMethodWhichDoesWork 花费的时间太长。在运行时,它可以防止在Dispatcher上进行任何其他工作。

The basic problem

The reason you are seeing the busy indicator stall is because your SomeMethodWhichDoesWork is taking too long. While it is running, it prevents any other work from occuring on the Dispatcher.

为处理动画而生成的输入和渲染优先级操作低于正常,但高于背景操作。但是,对调度程序的操作不会被优先级较高的操作中断。因此,即使Render操作是后台操作,它也必须等待正在运行的操作。

Input and Render priority operations generated to handle animations are lower than Normal, but higher priority than Background operations. However, operations on the Dispatcher are not interrupted by the enqueing of higher priority operations. So a Render operation will have to wait for a running operation, even if it is a Background operation.

ObserveOn(DispatcherScheduler)默认情况下会按正常优先级推送所有内容。较新的Rx版本具有重载功能,可让您指定优先级。

ObserveOn(DispatcherScheduler) will push everything through at Normal priority by default. More recent versions of Rx have on overload that allows you to specify a priority.

要强调的一点是,经常遗漏的是,项目一经分发就会由DispatcherScheduler排队到Dispatcher上。

One point to highlight that's often missed is that items will be queued onto the Dispatcher by the DispatcherScheduler as soon as they arrive NOT one after the other.

因此,如果您将3000个物品全部放在一起很近,则分派器上将以正常优先级备份3000个操作阻塞所有相同或较低优先级的对象,直到它们完成-包括渲染操作。几乎可以肯定这就是您所看到的-这意味着即使您做了所有工作,但UI更新仍然在后台线程上工作,这取决于您的UI更新量。

So if your 3000 items all turn up fairly close together, you will have 3000 operations at Normal priority backed up on the Dispatcher blocking everything of the same or lower priority until they are done - including Render operations. This is almost certainly what you were seeing - and that means you might still see problems even if you do all but the UI update work on a background thread depending on how heavy your UI updates are.

除此之外,您应该检查您是否不在UI线程上运行整个订阅-正如Lee所说。我通常会编写代码,以便在后台线程上 Subscribe 而不是使用SubscribeOn,尽管这也很好。

In addition to this, you should check you aren't running the whole subscription on the UI thread - as Lee says. I usually write my code so that I Subscribe on a background thread rather than use SubscribeOn, although this is perfectly fine too.

无论您做什么,都应在后台线程上做尽可能多的工作。在StackOverflow和其他地方,这一点已经到死。这里有一些很好的资源可以解决这个问题:

Whatever you do, do as much work as possible on a background thread. That point has been done to death on StackOverflow, and elsewhere. Here are some good resources covering this:

  • MSDN Entry on WPF Threading Model
  • MSDN Magazine "Build More Responsive Apps With The Dispatcher", by Shaun Wildermuth

如果要在遇到许多小更新时使UI保持响应状态,可以执行以下操作:

If you want to keep the UI responsive in the face of lots of small updates you can either:


  • 以较低的优先级安排项目,这很方便-但如果您需要特定的优先级,则不是很好

  • 将更新存储在自己的队列中并使其排队,并让您运行的每个操作调用队列中的下一个项目,这是最后一步。 / li>
  • Schedule items at a lower priority, which is nice and easy - but not so good if you need a certain priority
  • Store updates in your own queue and enqueue them and have each operation you run Invoke the next item from your queue as it's last step.

值得退后一步,同时也要看大图。

It's worth stepping back a bit and looking at the bigger picture as well.

如果您依次将3000个项目分别转储到UI中,则w为用户准备的是什么?充其量,他们将以刷新率100Hz(可能更低)运行监视器。我发现每秒10帧的速度足以满足大多数用途。

If you separately dump 3000 items into the UI in succession, what's that going to do for the user? At best they are going to be running a monitor with a refresh rate of 100Hz, probably lower. I find that frame rates of 10 per second are more than adequate for most purposes.

不仅如此,人类据说一次也无法处理5-9位以上的信息-因此,与一次性更新这么多东西相比,您可能会发现更好的汇总和显示信息的方法。例如,使用主视图/详细视图,而不是一次在屏幕上显示所有内容,等等。

Not only that, human beings supposedly can't handle more than 5-9 bits of information in one go - so you might find better ways of aggregating and displaying information than updating so many things at once. For example, make use of master/detail views rather than showing everything on screen at once etc. etc.

另一种选择是查看UI更新造成的工作量。某些控件(我在为您查看XamDataGrid)可能具有很长的度量/排列布局操作。您可以简化动画吗?使用更简单的可视树?考虑一下流行的忙碌的微调框,它看起来像是圆点,但实际上只是在改变它们的颜色。取得相当便宜的巨大效果。值得对您的应用程序进行性能分析,以了解时间的流逝。

Another option is to review how much work your UI update is causing. Some controls (I'm looking at you XamDataGrid) can have very lengthy measure/arrange layout operations. Can you simplify your animations? Use a simpler Visual tree? Think about the popular busy spinner that looks like circling dots - but really it's just changing their color. A great effect that is fairly cheap to achieve. It's worth profiling your application to see where time is going.

我也会考虑这种全面的方法。如果您有把握确定要立即更新这么多项目,为什么不缓存它们并分块管理它们呢?从源头开始,这可能会一直具有优势-也许在服务器上的某个地方?在任何情况下,Rx都有一些不错的运算符,例如 Buffer 可以将单个项目流转换成更大的列表-并且它的重载可以按时间进行缓冲和大小。

I would think about the overrall approach front-to-back as well. If you are reasonably certain you are going to get that many items to update at once, why not buffer them up and manage them in chunks? That would might have advantages all the way back to the source - which perhaps is on a server somewhere? In any case, Rx has some nice operators, like Buffer that can turn a stream of individual items into a larger lists - and it has overloads that can buffer by time and size together.

这篇关于处理许多小任务并保持UI响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆