Threadsafe观察者模式

时间:2015-01-02 09:18:40

标签: c# wpf multithreading windows-phone-8.1

我正在以MVC模式编写WPF应用程序。应用程序的目的是在数据库中显示一些数据,并且这些数据将异步更新。

我正在考虑如何设计架构,以便它是线程安全的。特别是:

  • 每个页面(或其viewmodel)必须能够订阅和取消订阅更新数据库的服务。
  • 更新数据库的服务通知所有订阅者,新数据到达并且他们应该刷新他们的视图。

显然,刚刚关闭的页面应取消订阅服务,而刚刚出现的页面应该(或可能)订阅。

我可以将订阅放在一个关键部分,以及广播新数据,但是想象一下下面的场景(页面〜=它的viewmodel,这里没什么关系):

  • 服务进入关键部分以广播有关新数据的信息(在单独的线程中)
  • 页面尝试输入关键部分以取消订阅(在主线程中)
  • 服务通知页面有关新数据的信息(在单独的线程中)。
  • Page填充其字段并引发PropertyChange事件(在单独的线程中)。
  • PropertyChange事件被编组到主线程。等待关键部分。

这对我来说似乎是一个僵局。

如何安全地设计此体系结构以避免此类死锁?也许页面永远不会取消订阅?或者是否有另一种方法来保护线程,使它们不会死锁?

2 个答案:

答案 0 :(得分:0)

鉴于该帖子被标记为WPF和WP-8.1以及评论中的澄清,我将执行以下操作:

  1. 让基础Model类(具有保存相关数据的属性的类)实现INotifyPropertyChanged
  2. 将所有页面的模型设为ObservableCollection<BaseModel>。该模型还应实现在构造函数中实例化的互斥锁/锁属性。
  3. 在所有视图模型中共享模型(例如,共享模型的实例)。
  4. 在&#39;服务&#39;执行异步操作时,我只会使用模型本身的锁定对象lock来自Model ObservableCollection的AddRemove项的代码部分。此部分必须放在Dispatcher.Invoke()或同等平台调用中。这可以确保它只是等待更新集合的UI线程。
  5. 我会将相关页面中的所有UI绑定到viewmodel中的模型引用。
  6. 这样,UI和视图模型对特定服务事件不小心,从而消除了订阅的开销,并且如果共享模型,也限制了数据的重复 - 即使屏幕上有20个页面,您的服务也会执行单个更新,通过框架的功能(绑定)传播到UI和视图模型。

答案 1 :(得分:0)

一个简单的解决方案可能是:不要在UI线程中执行取消订阅操作。 (一般不要阻止UI线程。)以异步方式执行,触发并忘记。

或者你可以看看Rx (Reactive Extensions)究竟是出于什么目的:以多线程方式实现观察者模式。


默默地“只是不取消订阅”可能不是一个好主意。虽然我不知道您的实现细节,但如果事件处理程序是实例方法,那么服务将保留对该实例的隐式引用,并且根据引用链可能会阻止您的页面或其他实例进行垃圾回收。 / p>


“还是有另一种方法来保护线程,使它们不会死锁?”目前在.NET框架中没有什么神奇的技巧可以自动防止死锁。其他多线程环境可能会也可能不会提供自动死锁解决方案(注意:不会阻止)服务,它可以检测死锁(发生之后)并自动选择受害者。在.NET中,等待资源时会发生异常。 (这还没有实现)