使用Reactive Extensions for .NET时,如何处理对IDisposable的引用:总是、从不或有时?

16
迄今为止,我一直在热心地保留 Rx 中任何 .Subscribe(...).Connect(...) 等方法返回的 IDisposable 引用。我这么做是因为我担心如果不保留引用,垃圾回收将会处理掉这个 disposable。
但是,我在 LINQPad 中进行了一些没有保留引用的 .Subscribe(...) 的调用,调用了一些 GC.Collect(),猜猜发生了什么?世界并没有结束,订阅顺利完成。
进一步测试后,我发现我的订阅在 .OnComplete() 后立即被处理掉了,而不需要我的干预。
这让我明白,至少对于 .Subscribe(...),保留订阅的唯一原因是强制订阅在正常完成之前结束。更像是一个取消标记。
那么,所有 Rx disposables 都是用于取消而非保持存活吗?
那么,保留 IDisposable 的规则是什么?
4个回答

18
除非您想在将来取消观察源,否则无需持有IDisposable对象。这同样适用于IScheduler上的Schedule方法,其返回的IDisposable对象可用于取消已安排的操作。
垃圾回收器不直接关心IDisposable对象,我们也没有在任何对象上实现终结器,因此在Rx的世界中,订阅等的生命周期管理完全由您自己决定。如果您愿意,可以将其与Win32句柄进行比较,其中Dispose类似于CloseHandle的道德等效物。
趣闻:在设计Rx期间的某个时刻,可取消操作会返回一个Action,其调用将导致取消。它具有相当大的功能灵感,但对一些人来说不太明显。因此,我们决定使用已经表示了资源管理概念的接口,IDisposable是显而易见的选择。尽管如此,在.NET Framework的其他地方,这与接口的典型用法有所不同:
- 对于永久序列,您通常只会将IDisposable对象丢弃。 - 在大多数情况下,由于框架的固有异步性质,您不会使用using语句处理Rx IDisposable对象。
希望这有所帮助, -Bart(Rx团队)

1
假设我保留了从source.Subscribe()返回的IDisposable。如果我的意图是在source完成其目的后将其GC'ed,但我仍然持有对IDisposable的引用,那么source可以被GC'ed吗? 还假设OnCompleted不会被调用。 - grimus
当我为事件订阅接口设计草图时,我要求订阅者提供一个谓词,以便他们可以指示他们是否仍然对该事件感兴趣。当添加订阅时,将检查一些现有的订阅者以查看他们是否仍然感兴趣(如果不感兴趣,则删除)。因此,一个订阅可以由一个具有对真正感兴趣的对象的“弱引用”的对象持有。出于好奇,您是否知道是否曾考虑过这样的设计用于任何MS事件相关模式中? - supercat

8

IScheduler.Schedule()返回的IDisposable仅在您想要取消已安排的操作(即在其发生之前)时才有用。

IObservable.SubscribeIConnectableObservable.Connect返回的IDisposable是等效的,即释放任一一个都将终止对源可观察对象的订阅。

至于垃圾回收,虽然Rx使其更难以衡量,但它仍受GC规则的限制。如果您的可观察源是根源的(例如UI控件的事件),则无需担心它被GC回收。

如果您的可观察源是Observable.Interval或其他基本上是递归IScheduler调用的东西,则调度程序应该将其保持活动状态(即线程池、任务池或分派程序),因为可观察对象通过IScheduler.Schedule与调度程序相关联。


4
首先,我认为需要指出的是垃圾收集和对象处理/IDisposable接口是完全分离的 - 特别地,垃圾回收器永远不会通过调用IDisposable的Dispose方法来直接处理一个对象(除非该对象的终结器本身这样做)。
至于何时应该保留IDisposable对象,您应该保留对任何需要处理的IDisposable对象的引用 - 这听起来很明显,因为它确实如此!:-) 除非可处理对象的生命周期长于单个方法,否则通常使用using关键字:
using (var myDisposableObject = GetSomeDisposableObject())
{
    myDisposableObject.DoThings();
}

这限制了myDisposableObject的范围(有助于避免尝试使用已被处理的对象),并确保即使抛出异常,该对象也能正确处理。

也许对于某些类/ API,您不需要(或不应)处理返回给您的对象,但这完全取决于返回可处理对象的API /类。


在问题编辑强调system.reactive命名空间之前,我已经添加了这个答案。我在这个领域没有太多经验,所以无法提供帮助,但我仍然会留下这个答案,希望它能帮助解释GC和IDisposable之间的区别。 - Justin
我想强调一点,如果你已经使用完一个引用,请手动调用dispose方法或者通过using语句释放该引用。没有必要将这些对象保留在你的图形中。 - Ritch Melton
这个问题并没有被编辑 - 重点一开始就在 Rx 上 - 但我仍然感谢你的回答和决定不将其删除。 - Enigmativity
@Enigmativity 哦,是的,看起来我第一次查看问题时没有注意到system.reactive标签! :-) 无论如何,希望能有所帮助。 - Justin
是的,它帮助我更好地理解了一般行为。不过我仍然很想得到一个针对 Rx 的具体答案。谢谢。 - Enigmativity

2

是的,从Subscribe()返回的IDisposable仅用于取消订阅。当然,如果需要取消订阅,您将使用Dispose()而不是Unsubscribe()调用。


你知道所有返回 IDisposable 的 Rx 方法是否都是这种情况吗?比如 IConnectableObservervable.Connect()IScheduler.Schedule(Action) 等等? - Enigmativity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接