在数字信号采集系统中,数据通常由一个线程推送到系统中的观察者。
来自Wikipedia/Observer_pattern的例子:
foreach (IObserver observer in observers)
observer.Update(message);
例如来自例如的用户动作GUI线程要求数据停止流动,你想要打破主题 - 观察者连接,甚至完全处置观察者。
有人可能会说:您应该停止数据源,并等待Sentinel值来处置连接。但这会在系统中产生更多延迟。
当然,如果数据抽取线程刚刚询问了观察者的地址,它可能会发现它正在向被破坏的对象发送消息。
有人创建了一个“官方”设计模式来应对这种情况吗?不应该吗?
答案 0 :(得分:2)
如果您希望数据源始终处于并发安全的一面,那么您应该至少有一个指针对他来说总是安全的。 因此,Observer对象的生命周期应该在数据源之前没有结束。
这可以通过仅添加观察者来完成,但永远不会删除它们。 您可以让每个观察者本身不执行核心实现,但让它将此任务委托给ObserverImpl对象。 您锁定对此impl对象的访问权限。这没什么大不了的,它只是意味着如果观察者忙于使用ObserverImpl对象,GUI unsubscriber会被阻塞一段时间。如果GUI响应性是一个问题,您可以使用某种并发作业队列机制,并在其上推送取消订阅作业。 (如Windows中的PostMessage)
取消订阅时,您只需将核心实现替换为虚拟实现。再次这个操作应该抓住锁。这确实会引入一些等待数据源,但由于它只是一个[锁定指针交换 - 解锁],你可以说这对于实时应用来说足够快。
如果你想避免堆叠只包含虚拟对象的Observer对象,你必须做一些簿记,但这可以归结为一些微不足道的东西,比如一个对象,它从列表中拿出一个指向Observer对象的指针。
优化: 如果你同时保持实现(真实的那个+虚拟的)只有Observer本身,你可以在没有实际锁定的情况下执行此操作,并使用类似InterlockedExchangePointer的东西来交换指针。 最糟糕的情况:在交换指针时委托调用 - >没有什么大不了的,所有对象都活着,委托可以继续。下一次委托调用将是新的实现对象。 (当然,除非有任何新的掉期)
答案 1 :(得分:0)
您可以向所有观察者发送消息,通知他们数据源正在终止,让观察者从列表中删除自己。
在回应评论时,主题观察员模式的实施应允许动态增加/删除观察员。在C#中,事件系统是主题/观察者模式,其中使用event += observer
添加观察者并使用event -= observer
删除。