WPF Dispatcher 是多线程问题的解决方案吗?
我对在代码中使用锁有一种非常不好的感觉,但现在 WindowBase 的调度程序存在,我想在任何地方使用它。
例如,我使用多线程单例 WCF 服务,该服务在 PRISM 的 EventAggregator 上发布事件,有效负载是不可变的(它只是数据),并且具有调度程序的每个线程都可以优雅地检索事件,而不会在自己的调度程序中出现死锁。 (不仅是 UI 线程,还包括数据库调用线程、服务调用线程、日志线程或其他调用缓慢的线程,因为我不想冻结 UI)。
但我的问题是这个调度程序与 WPF 结合在一起,所以当我在任何地方使用它时我感到有点内疚,我觉得调度程序不是为我的用例创建的。
是否存在另一个不与 WPF 耦合的 Dispatcher 实现? 或者可以滥用它吗?
谢谢,
更新
Paul Stovell 给我的解决方案是创建一个 IDispatcher 接口和一个 Wpf Dispatcher 适配器,这样会更容易测试! 这个解决方案对我来说很好,因为我重构了我的测试,现在我可以在测试中使用 SynchronousDispatcherAdapter(多亏了它,我不必在测试中使用 WPF 的 Dispatcher)。
使用 Dispatcher 而不是 BackgroundWorker 是有意义的,因为我使用的是多发布者/订阅者模式(使用 PRISM),并且感谢 Dispatcher,每个事件处理程序都会在订阅事件的线程上调用。 这意味着唯一可能发生多线程问题的点是我的事件的有效负载(我使他不可变)。
我的不同线程之间不直接通信,它们只能发布和订阅事件。 因此,数据库调用、日志调用、服务调用、UI 调用在不同的线程上运行,并且彼此不了解(它们只知道它们订阅和发布的事件)。
当我从 UI 向存储库进行一些调用时,后台工作人员将有意义。
但我希望找到一种不使用BackgroundWorker的设计,因为我更喜欢使用这种订阅者/发布者模式(我认为这使我的代码更具可读性)
I have a very bad feeling about using lock in my code but now the Dispatcher of WindowBase exists and I want to use it everywhere.
For example I use a multi thread singleton WCF service who publish events on the EventAggregator of PRISM, the payload is immutable (it is just data) and every thread with a dispatcher can retrieve the event gracefully, whitout deadlock in their own dispatcher. (Not only UI thread, but also threads with database calls, threads with services call, threads which log or other threads with slow calls, because I don't want to freeze the UI).
But my problem is that this Dispatcher is coupled with WPF so I feel a bit guilty when I use it everywhere, I feel that the dispatcher was not created for my use case in mind.
Does it exist another Dispatcher implementation not coupled with WPF ? or that's OK to abuse it ?
Thanks,
Update
The solution that Paul Stovell give to me is to create an interface IDispatcher, and an adapter for the Wpf Dispatcher, so this will be easier to test !
This solution was good for me because, I refactored my tests and I can now use a SynchronousDispatcherAdapter in my tests (Thanks to it, I don't have to use the Dispatcher of WPF in my tests).
Using the Dispatcher instead of a BackgroundWorker make sense, because I'm using a multi publisher / subscriber pattern (with PRISM), and thanks to the Dispatcher every event handler are called on threads who subscribe them to the event. This means that the only point where multi threading issue can happen is at the payload of my event (I made him immutable).
My different threads don't communicate directly between them they can just publish and subscribe to event.
Thus, database calls, logs calls, services calls, UI calls run on different threads and don't know about each other (they only know about events they subscribe and publish).
The background worker will make sense, when I will make some calls from my UI to a repository.
But I hope to find a design without using BackgroundWorker because I prefere to use this subscriber/publisher pattern (I think it makes my code more readable)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
使用 Dispatcher(或 BackgroundWorker)的主要问题是很难测试,除非您的测试工具实际上有一个 UI 线程。
解决方案 1
使用 SynchronizationContext。 它提供了在 UI 线程上调用的相同能力,并且可以在 Windows 或 WPF 中工作。 还对其进行测试可能的。
解决方案 2
将调度程序视为另一种服务。 当您使用 PRISM 时,您会熟悉服务和 IOC。 以下是如何使用此类服务:
这允许您用不同的实现替代您的平台/测试。
以下是 IDispatcher 的示例,一个 WPF 实现 和 测试实现。 您可以像任何其他服务一样将它们注册到 IOC 容器中,并且它们可用于 UI 和其他服务。
The main issue with using the Dispatcher (or BackgroundWorker) is that it's difficult to test, unless your testing harness actually has a UI thread.
Solution 1
Use the SynchronizationContext. It provides the same ability to invoke on the UI thread and works in Windows or WPF. Testing it also possible.
Solution 2
Think of the dispatcher as being just another service. As you use PRISM, you are familiar with services and IOC. Here is how such a service may be used:
This allows you to substitute in different implementations for your platform/testing.
Here is an example of IDispatcher, a WPF implementation and a test implementation. You would register them with your IOC container just like any other service, and they are available to both UI and other services.
是和否..它是一个渲染事物..本身不是一个线程事物..
调度程序按优先级选择工作项并运行每个工作项直至完成。 每个 UI 线程必须至少有一个 Dispatcher,并且每个 Dispatcher 只能在一个线程中执行工作项。 根据 Microsoft 的此链接。
您仍然必须自己处理自己启动的任何线程。
检查这个了解以下信息:使用基于事件的多线程编程异步模式
我个人使用 后台工作人员线程需求。
此处为最佳实践。
yes and no.. its a rendering thing..not a threading thing per se..
The Dispatcher selects work items on a priority basis and runs each one to completion. Every UI thread must have at least one Dispatcher, and each Dispatcher can execute work items in exactly one thread. as per this this link from Microsoft.
You still have to handle on your own any threads you start yourself.
Check this one for info on: Multithreaded Programming with the Event-based Asynchronous Pattern
Personally I use the Background Worker for my threading needs.
Best Practices here.
我要彻底解决这个问题,但这听起来是个坏主意。 您的意思是,您需要一个队列,供发布者为其订阅者转储项目。 调度程序的核心只是一个美化的队列,周围有很多开销。 开销专门用于保护对您未使用的 UI 资源的访问。 这表明它的使用是错误的。
建议 SynchronizationContext 的人走上了一条好的道路。 这可以实现您想要的(安全地将数据编组到另一个线程),而不会将您束缚于 UI 概念。 您可以编写一个扩展方法,将您的事件编组到事件的每个订阅者请求的 SynchronizationContext(可以通过将订阅委托的目标强制转换为 ISynchronizeInvoke 来使用它。该强制转换的结果将允许您知道是否需要将其 。
更好的是只使用具有适当锁定语义的队列,锁的开销不太可能成为问题,如果是的话,您对调度程序的使用会更多 在这种情况下,更简单的方法是更好的,他们的关键是只保留锁来添加/删除队列中的项目。
I'm going to necro the heck out of this, but this sounds like a bad idea. What you are saying is that you need a queue for your publisher to dump items on for its subscribers. A Dispatcher, at its core, is just a glorified queue, with a LOT of overhead around it. The overhead is specifically for protecting access to UI resources, which you aren't using. That suggests it's the wrong thing to use.
The person who suggested a SynchronizationContext is on a good path. This accomplishes what you want (safely marshalling data to another thread), without tying you to a UI concept. You could write an extension method which marshals your event to the SynchronizationContext requested by each subscriber of the event (it is available by casting the Target of your subscribing Delegate to ISynchronizeInvoke. The result of that cast will allow you to know if it needs to be marhalled, and can do it automatically for you.
Even better would be to just use a queue with appropriate locking semantics. The overhead of the lock is unlikely to be an issue, and if it is, your use of the Dispatcher would be far more destructive than a simple lock. In this case, simpler is better. They key would be to only keep the lock to add/remove an item from the queue. Your subscribers should perform whatever work they do outside of the lock.