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