问题描述
我对在代码中使用锁有一种很不好的感觉,但是现在WindowBase的Dispatcher存在了,我想在任何地方使用它.
例如,我使用一个多线程单例WCF服务,该服务在PRISM的EventAggregator上发布事件,有效载荷是不可变的(它只是数据),每个带有调度程序的线程都可以优雅地检索事件,而在自己的调度程序中避免死锁. (不仅UI线程,还有数据库调用的线程,服务调用的线程,日志记录的线程或慢速调用的其他线程,因为我不想冻结UI.)
但是我的问题是,此Dispatcher与WPF结合使用,因此当我在各处使用它时,我都会感到内feel,我觉得并不是为我的用例创建的.
是否存在另一个未与WPF耦合的Dispatcher实现?还是可以滥用它?
谢谢
更新
Paul Stovell给我的解决方案是创建一个接口IDispatcher和一个Wpf Dispatcher适配器,因此这将更易于测试!该解决方案对我来说是有好处的,因为我重构了测试,现在可以在测试中使用SynchronousDispatcherAdapter(感谢它,我不必在测试中使用WPF的Dispatcher).
使用Dispatcher代替BackgroundWorker是有意义的,因为我使用的是多发布者/订阅者模式(带有PRISM),并且由于Dispatcher的缘故,每个事件处理程序都在为其订阅事件的线程上被调用.这意味着可能发生多线程问题的唯一点是事件的有效负载(我使他不可变).
我的不同线程之间无法直接通信,它们只能发布和订阅事件.因此,数据库调用,日志调用,服务调用,UI调用在不同的线程上运行,彼此之间不了解(它们仅了解它们订阅和发布的事件).
当我将一些从UI调用到存储库的调用时,后台工作器将很有意义.
但是我希望在不使用BackgroundWorker的情况下找到设计,因为我更喜欢使用这种订阅者/发布者模式(我认为这会使我的代码更具可读性)
使用Dispatcher(或BackgroundWorker)的主要问题是很难测试,除非您的测试工具实际上具有UI线程.
解决方案1
使用 SynchronizationContext .它具有在UI线程上调用的相同功能,并且可以在Windows或WPF中工作.测试它也可能.
解决方案2
认为调度程序只是另一种服务.使用PRISM时,您会熟悉服务和IOC.可以使用这种服务的方式如下:
// Not a UI component
public class MyDomainService : IMyDomainService
{
private readonly IDispatcher _dispatcher;
public MyDomainService(IDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
private void GotResultFromBackgroundThread()
{
_dispatcher.Dispatch(() => DoStuffOnForegroundThread());
}
}
这允许您用不同的实现方式替代您的平台/测试.
以下是 IDispatcher 的示例, href ="http://www.codeplex.com/bindablelinq/SourceControl/changeset/view/25046#293214" rel ="noreferrer"> WPF实现和测试实现.您可以像其他任何服务一样在IOC容器中注册它们,并且UI和其他服务都可以使用它们.
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)
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:
// Not a UI component
public class MyDomainService : IMyDomainService
{
private readonly IDispatcher _dispatcher;
public MyDomainService(IDispatcher dispatcher)
{
_dispatcher = dispatcher;
}
private void GotResultFromBackgroundThread()
{
_dispatcher.Dispatch(() => DoStuffOnForegroundThread());
}
}
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.
这篇关于WPF分派器是多线程问题的解决方案吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!