多线程观察者的设计模式
-
09-06-2019 - |
题
在数字信号采集系统中,通常通过一个线程将数据推送到系统中的观察者中。
示例来自 维基百科/Observer_pattern:
foreach (IObserver observer in observers)
observer.Update(message);
当例如来自例如的用户操作GUI 线程要求数据停止流动,您想要中断主体-观察者连接,甚至完全处置观察者。
有人可能会争辩说:您应该停止数据源,并等待哨兵值来处理连接。但这会导致系统出现更多延迟。
当然,如果数据泵线程刚刚请求观察者的地址,它可能会发现它正在向被破坏的对象发送消息。
有人创建了一个“官方”设计模式来应对这种情况吗?他们不应该吗?
解决方案
如果你想让数据源始终处于并发安全的一面,那么你应该至少有一个始终安全供他使用的指针。因此,观察者对象的生命周期应该不会早于数据源的生命周期。
这可以通过仅添加观察者而不删除它们来完成。您可以让每个观察者本身不执行核心实现,而是将此任务委托给 ObserverImpl 对象。您锁定对此 impl 对象的访问。这没什么大不了的,它只是意味着 GUI 取消订阅者会被阻塞一段时间,以防观察者忙于使用 ObserverImpl 对象。如果 GUI 响应能力成为问题,您可以使用某种并发作业队列机制,并将取消订阅作业推送到其上。(就像 Windows 中的 PostMessage 一样)
取消订阅时,您只需用核心实现替换虚拟实现即可。同样,此操作应该获取锁。这确实会引入一些等待数据源的过程,但由于它只是一个[锁定 - 指针交换 - 解锁],你可以说这对于实时应用程序来说已经足够快了。
如果您想避免堆叠仅包含虚拟对象的观察者对象,则必须进行某种簿记,但这可能会归结为一些琐碎的事情,例如持有指向他需要的列表中观察者对象的指针的对象。
优化 :如果您还保持实现(真实的 + 虚拟的)与观察者本身一样长,那么您可以在没有实际锁定的情况下执行此操作,并使用 InterlockedExchangePointer 之类的东西来交换指针。最坏的情况 :当指针交换时,委托调用正在进行 --> 没什么大不了的,所有对象都保持活动状态并且委托可以继续。下一个委托调用将是新的实现对象。(当然,除非有任何新的交换)
其他提示
您可以向所有观察者发送一条消息,通知他们数据源正在终止,并让观察者将自己从列表中删除。
作为对评论的回应,主体-观察者模式的实现应该允许动态添加/删除观察者。在 C# 中,事件系统是主体/观察者模式,其中观察者使用以下方式添加 event += observer
并使用删除 event -= observer
.