线程安全观察器模式

本文关键字:模式 观察 安全观 安全 线程 | 更新日期: 2023-09-27 18:35:49

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

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

  • 每个页面(或其视图模型)必须能够订阅和取消订阅服务,这将更新数据库。
  • 更新数据库的服务通知所有订阅者,新数据已到达,他们应刷新其视图。
显然,刚刚关闭的页面应该

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

我可以将订阅放在关键部分,也可以广播新数据,但随后想象一下以下场景(页面 ~= 它的视图模型,这里无关紧要):

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

对我来说,这看起来像是一个僵局。

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

线程安全观察器模式

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

  1. 让基模型类(具有保存相关数据的属性的类)实现INotifyPropertyChanged
  2. 将所有页面的模型作为ObservableCollection<BaseModel>。该模型还应实现在构造函数中实例化的互斥/锁属性。
  3. 在所有视图模型之间共享模型(例如,共享模型的实例)。
  4. 在执行异步操作的"服务"中,我只会lock使用模型本身的锁定对象从模型可观察集合中AddRemove项的代码部分。此部分必须放在 Dispatcher.Invoke() 或等效平台调用中。这可确保只有 UI 线程在等待更新集合。
  5. 我会将相关页面中的所有 UI 绑定到视图模型中的模型引用。
这样,UI 和视图模型就不会

对特定的服务事件产生粗心,从而消除了订阅的开销,并且如果您共享模型,您还可以限制数据的重复 - 即使屏幕上有 20 页,您的服务也会执行单个更新,该更新通过框架(绑定)的力量传播到 UI 和视图模型。

一个简单的解决方案可能是: 不要在 UI 线程中执行取消订阅操作。(通常不会阻止 UI 线程。以异步方式进行,即发即弃。

或者,你可以看看Rx(反应式扩展)到底是什么用于这个目的:以多线程方式实现观察者模式。


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


"或者有没有另一种方法来保护线程,使它们不会死锁?"目前在.NET框架中,没有自动防止死锁的魔术。其他多线程环境可能会也可能不会提供自动死锁解决(注意:不是预防)服务,该服务可以检测死锁(在死锁发生后)并自动选择受害者。在 .NET 中,等待资源发生的情况可能是一个例外。(同样,这尚未实现)