使用Reactive Expressions的'Linqed'IObservable的生命周期

时间:2014-01-06 00:04:02

标签: c# linq system.reactive

这可能是一个非常愚蠢的问题,但我不确定我是否应该继续使用Linq扩展方法生成的IObservable。默认情况下,大多数示例似乎都这样做,但在长时间运行的程序中会发生什么。我有两个例子来解释我的问题:

我的第一个,也是当前的默认实现,涉及订阅pub \ sub事件聚合器提供的IObservable。我对此很满意,因为我知道只要聚合器是可观察的,就会存在。

protected virtual void SubscribeToEvents()
{
    _subscriptions.Dispose();
    _subscriptions = new CompositeDisposable();

    IDisposable sendSubscription = Events.GetStream<OutgoingByteMessage>()
        .Subscribe(msg => 
        {
            if (msg.Identifier.ChannelId == this.Id)
            {
                var queueItem = Tuple.Create(msg.Identifier.ConnectionId, msg.Data);
                _sendQueue.Enqueue(queueItem);
            }
        });            

    _subscriptions.Add(sendSubscription);                       
} 

但是,如果我使用Linq来打破某些逻辑,我可以这样做以获得订阅:

IDisposable altSendSubscription = Events.GetStream<OutgoingByteMessage>()
    .Where(msg => msg.Identifier.ChannelId == this.Id)
    .Select(msg => Tuple.Create(msg.Identifier.ConnectionId, msg.Data))
    .Subscribe(item =>
    {                    
        _sendQueue.Enqueue(item);                    
    });

我的问题是,在第二个版本上,我应该保留那个.Select方法的产品吗?这种情况在一个方法中发生,毕竟在技术上不会返回那里的订单,只要SubscribeToEvents完成并且有资格进行垃圾收集,它就会超出范围?或者IDisposable对象本身是否保留对它的引用,从而使得在某处不能保留对它的具体引用是安全的。

在查看Rx源之后,看起来他们使用一系列转发器类来完成Linq魔术,但我仍然有点偏执。你们觉得怎么样?

2 个答案:

答案 0 :(得分:3)

在一天结束时,所有IObservable对象都通过Events.GetStream上的订阅者列表保留(当然,取决于您Events.GetStream的实现)。这是否超出范围取决于您的实施。

但是,持有IDisposable几乎肯定会将订阅保留在范围内,因此,即使您的实施是return Observable.Never<OutgoingByteMessage>,由于IDisposable,它仍然存在。

您通常不必担心活动订阅超出范围 - 您应该担心的是订阅泄漏。当您订阅全局对象(例如从IoC容器返回的内容)并且不断开订阅时(如果Observable永远不会终止),就会发生这些情况。

这听起来似乎不经常发生,但它的出现超出了你的期望。例如:

MessageBus.Current.Listen<FooMessage>()
    .Where(x => x.Bar == "Baz")
    .Subscribe(x => Console.WriteLine(x));

如果我把它放在一个始终创建的对象的构造函数中,我会创建许多永远不会被清理的订阅。

答案 1 :(得分:2)

不,你不用担心。想一想这样:只要有事件来源,可观察链就无法收集,因为订阅行为将它们全部联系在一起。也就是说,每个操作符都订阅上面的操作符,创建一个它是父引用的观察者。

您甚至不需要保留订阅句柄(altSendSubscription)。

为什么不使用内存分析器并查看堆来说服自己?