WPF Dispatcher是多线程问题的解决方案吗?

时间:2009-01-28 07:23:11

标签: wpf multithreading dispatcher

我对在代码中使用lock感觉很糟糕,但现在WindowBase的Dispatcher存在,我想在任何地方使用它。

例如,我使用多线程单例WCF服务,它在PRISM的EventAggregator上发布事件,有效负载是不可变的(它只是数据),每个带调度程序的线程都可以优雅地检索事件,在自己的调度程序中完成死锁。 (不仅是UI线程,还有带有数据库调用的线程,带有服务调用的线程,带有慢速调用的日志或其他线程的线程,因为我不想冻结UI)。

但我的问题是这个Dispatcher与WPF结合在一起,所以当我到处使用它时我感到有点内疚,我觉得调度程序不是为我的用例而创建的。

是否存在另一个与WPF无法结合的Dispatcher实现?或者可以滥用它?

谢谢,

更新

Paul Stovell给我的解决方案是创建一个接口IDispatcher,以及一个Wpf Dispatcher的适配器,所以这将更容易测试! 这个解决方案对我有好处,因为我重构了我的测试,我现在可以在我的测试中使用SynchronousDispatcherAdapter(感谢它,我不必在我的测试中使用WPF的Dispatcher)。

使用Dispatcher而不是BackgroundWorker是有道理的,因为我使用的是多发布者/订阅者模式(使用PRISM),并且由于Dispatcher,每个事件处理程序都会在订阅它们的事件上调用。这意味着可能发生多线程问题的唯一一点是我的事件的有效负载(我使他成为不可变的)。

我的不同主题不直接在他们之间进行通信,他们只能发布和订阅事件。 因此,数据库调用,日志调用,服务调用,UI调用在不同的线程上运行,彼此不了解(他们只知道他们订阅和发布的事件)。

当我从UI访问存储库时,后台工作人员会有意义。

但我希望在不使用BackgroundWorker的情况下找到一个设计,因为我更喜欢使用这个订阅者/发布者模式(我认为它使我的代码更具可读性)

3 个答案:

答案 0 :(得分:16)

使用Dispatcher(或BackgroundWorker)的主要问题是它很难测试,除非您的测试工具实际上有一个UI线程。

解决方案1 ​​

使用SynchronizationContext。它提供了在UI线程上调用并在Windows或WPF中工作的相同功能。测试它also possible

解决方案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());
   }
}

这允许您在不同的实现中替换您的平台/测试。

以下是IDispatcherWPF implementationtest implementation的示例。您可以像使用任何其他服务一样在IOC容器中注册它们,它们可用于UI和其他服务。

答案 1 :(得分:2)

是和否..它是一个渲染的东西......本身并不是一个穿线的东西..

Dispatcher优先选择工作项并运行每个工作项完成。每个UI线程必须至少有一个Dispatcher,每个Dispatcher只能在一个线程中执行工作项。根据Microsoft的this link

你仍然必须自己处理自己开始的任何线程。

检查this one以获取以下信息:使用基于事件的异步模式进行多线程编程

我个人使用Background Worker来满足我的线程需求。

Best Practices here

答案 2 :(得分:0)

我要彻底解决这个问题,但这听起来是个坏主意。您的意思是说,您需要一个队列供发布者为其订阅者转储项目。分派器的核心只是光荣的队列,周围有很多开销。开销专门用于保护对未使用的UI资源的访问。这表明使用它是错误的事情。

建议SynchronizationContext的人走的很好。这样就完成了您想要的工作(将数据安全地编组到另一个线程),而无需将您与UI概念联系在一起。您可以编写一个扩展方法,将您的事件编组到事件的每个订阅者所请求的SynchronizationContext(可通过将您的委托的Target转换为ISynchronizeInvoke来使用。转换的结果将使您知道是否需要编组,并可以自动为您完成。

更好的方法是只使用具有适当锁定语义的队列。锁的开销不太可能成为问题,如果是这样,则对Dispatcher的使用将比简单的锁更具破坏性。在这种情况下,越简单越好。他们的关键是只保留锁以从队列中添加/删除项目。您的订户应该执行他们在锁之外所做的任何工作。