WPF 后台工作程序与调度程序
在我的 WPF 应用程序中,我需要执行异步操作,然后需要更新 GUI。而这件事我必须在不同的时刻以不同的操作做很多次。我知道有两种方法可以做到这一点:Dispatcher 和BackgroundWorker。
因为当我选择了就很难回头,我问你:什么更好?选择其中一种而不是另一种的原因是什么?
谢谢你! 皮莱吉
In my WPF application I need to do an async-operation then I need to update the GUI. And this thing I have to do many times in different moment with different oparations. I know two ways to do this: Dispatcher and BackgroundWorker.
Because when I will chose it will be hard for me to go back, I ask you: what is better? What are the reasons for choosing one rather than the other?
Thank you!
Pileggi
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Dispatcher 与其他线程方法的主要区别在于 Dispatcher 实际上并不是多线程的。调度程序管理控件,这些控件需要单个线程才能正常运行; Dispatcher 的 BeginInvoke 方法将事件排队以供稍后执行(取决于优先级等),但仍在同一线程上。
另一方面,BackgroundWorker 实际上在调用它的同时在单独的线程中执行代码。它也比真正的线程更容易使用,因为它自动与应用程序的主线程同步(至少我认为我记得正确),该主线程负责控件和消息队列(WPF 和 Dispatcher 线程) Silverlight),因此在从后台线程更新控件时无需使用 Dispatcher.Invoke(或 WinForms 中的 Control.Invoke),尽管并不总是建议这样做。
正如里德所说,任务并行库是一个很好的替代选择。
编辑:进一步观察。
正如我上面所说,Dispatcher 并不是真正的多线程;它是多线程的。它只会给人一种错觉,因为它确实运行您在其他时间传递给它的委托。仅当代码实际上仅处理应用程序的视图方面(即控件、页面、窗口等)时,我才会使用调度程序。当然,它的主要用途实际上是触发来自其他线程的操作,以正确或在正确的时间更新控件(例如,仅在某些控件完全呈现/布局自身之后才设置焦点)使用 Dispatcher 可以轻松完成,因为在 WPF 中渲染并不完全确定)。
BackgroundWorker 可以使多线程代码比平常简单得多;这是一个容易掌握的简单概念,最重要的是(如果有意义的话)您可以从中派生自定义工作人员,这些工作人员可以是异步执行单个任务的专用类,其属性可以用作参数、进度通知和取消等我总是发现BackgroundWorker有很大的帮助(除了当我必须从它派生以保持原始线程的文化以正确维护本地化时:P)
最强大但也是困难的路径是使用可用的最低级别System。 .Threading.Thread;然而,它很容易出错,因此不建议这样做。多线程编程是困难,这是理所当然的。但是,如果您想了解所有方面,这里有很多很好的信息:这篇优秀的文章 作者:我们的好伙伴乔恩·斯基特(Jon Skeet)立即跳入脑海(文章的最后一页也有很多非常有趣的链接)。
在.Net 4.0中,我们有一个不同的选项,任务并行库。我还没有经常使用它,但从我所看到的来看,它令人印象深刻(而且 PLINQ 简直太棒了)。如果您有好奇心和资源来学习它,这就是我的建议(毕竟不应该花费那么多时间来学习)。
The main difference between the Dispatcher and other threading methods is that the Dispatcher is not actually multi-threaded. The Dispatcher governs the controls, which need a single thread to function properly; the BeginInvoke method of the Dispatcher queues events for later execution (depending on priority etc.), but still on the same thread.
BackgroundWorker on the other hand actually executes the code at the same time it is invoked, in a separate thread. It also is easier to use than true threads because it automatically synchronizes (at least I think I remember this correctly) with the main thread of an application, the one responsible for the controls and message queue (the Dispatcher thread in the case of WPF and Silverlight), so there's no need to use Dispatcher.Invoke (or Control.Invoke in WinForms) when updating controls from the background thread, although that may not be always recommended.
As Reed said, Task Parallel Library is a great alternative option.
Edit: further observations.
As I said above, the Dispatcher isn't really multithreaded; it only gives the illusion of it, because it does run delegates you pass to it at another time. I'd use the Dispatcher only when the code really only deals with the View aspect of an application - i.e. controls, pages, windows, and all that. And of course, its main use is actually triggering actions from other threads to update controls correctly or at the right time (for example, setting focus only after some control has rendered/laid-out itself completely is most easily accomplished using the Dispatcher, because in WPF rendering isn't exactly deterministic).
BackgroundWorker can make multithreaded code a lot simpler than it normally is; it's a simple concept to grasp, and most of all (if it makes sense) you can derive custom workers from it, which can be specialized classes that perform a single task asynchronously, with properties that can function as parameters, progress notification and cancellation etc. I always found BackgroundWorker a huge help (except when I had to derive from it to keep the Culture of the original thread to maintain the localization properly :P)
The most powerful, but also difficult path is to use the lowest level available, System.Threading.Thread; however it's so easy to get things wrong that it's not really recommended. Multithreaded programming is hard, that's a given. However, there's plenty of good information on it if you want to understand all the aspects: this excellent article by our good fellow Jon Skeet immediately jumps to mind (the last page of the article also has a good number of very interesting links).
In .Net 4.0 we have a different option, Task Parallel Library. I haven't worked with it much yet but from what I've seen it's impressive (and PLINQ is simply great). If you have the curiosity and resources to learn it, that's what I'd recommend (it shouldn't take that much to learn after all).
如果您正在执行单个操作,BackgroundWorker 会很好,它提供进度通知和完成事件。但是,如果您要多次运行同一操作或多个操作,那么您将需要多个BackgroundWorker。在这种情况下,它可能会变得很麻烦。
如果您不需要进度事件,那么使用 ThreadPool 和 Dispatcher 可能会更简单 - 特别是如果您要执行很多不同的操作。
但是,如果可以选择 C# 4,那么使用任务并行库也是一个不错的选择。这使您可以使用当前 SynchronizationContext 来使用连续任务设置,这在许多情况下提供了更简单、更清晰的模型。有关详细信息,请参阅 我关于该主题的博客文章。
BackgroundWorker is nice if you're doing a single operation, which provides progress notifications and a completion event. However, if you're going to be running the same operation multiple times, or multiple operations, then you'll need more than one BackgroundWorker. In this case, it can get cumbersome.
If you don't need the progress events, then using the ThreadPool and Dispatcher can be simpler - especially if you're going to be doing quite a few different operations.
If C# 4 is an option, however, using the Task Parallel Library is a great option, as well. This lets you use continuation tasks setup using the current SynchronizationContext, which provides a much simpler, cleaner model in many cases. For details, see my blog post on the subject.