如何使用IObservable <idisposable>停止泄漏IDisposable?

时间:2016-06-21 05:53:31

标签: c# system.reactive idisposable resource-leak

我的生产代码中有一个错误,我跟踪并设法创建一个重现的测试用例。我正在创作

{
  "errorCode": 40102,
  "message": "Invalid Signature.",
  "reference": "A:554042e3-965c-4e88-a7b8-3f2b52c278f3_JEC6uQxEUkSkNmSkvvNwXA"
}

实例并在订阅中使用序列一次性,以便一次最多保留一个项目。这是向场景添加图形对象并在更新可用时将其删除的简洁方法。

然而,下面的测试用例显示了一个微妙的错误。

IObservable<IDisposable>

以及问题核心的 SubscribeDisposable 方法。

using System;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using FluentAssertions;
using Microsoft.Reactive.Testing;
using Xunit;

namespace WeinCadSW.Spec
{
    /// <summary>
    /// This test case demonstrates problems with streams of IDisposables.
    /// http://stackoverflow.com/questions/37936537/how-to-stop-leaking-idisposables-with-an-iobservableidisposable
    /// </summary>
    public class ObservableDisposableSpec : ReactiveTest
    {
        TestScheduler _Scheduler = new TestScheduler();
        [Fact]
        public void ShouldWork()
        {
            var o = _Scheduler.CreateHotObservable
                (OnNext(100, "A")
                , OnNext(200, "B")
                , OnNext(250, "C")
                , OnNext(255, "D")
                , OnNext(258, "E")
                , OnNext(600, "F")
                );

            var disposablesCreated = 0;
            var disposabledDisposed = 0;
            var oo = o.Select
                (s =>
                {
                    disposablesCreated++;
                    return Disposable.Create(() => disposabledDisposed++);
                })
                .Delay(TimeSpan.FromTicks(10), _Scheduler);


            IDisposable sub = Disposable.Empty;
            _Scheduler.ScheduleAbsolute(null, 0, (Func<IScheduler, object, IDisposable>)((scheduler, state) =>
            {
                sub = oo.SubscribeDisposable();
                return Disposable.Empty;
            }));
            _Scheduler.ScheduleAbsolute(null, 605, (Func<IScheduler, object, IDisposable>)((scheduler, state) =>
            {
                sub.Dispose();
                return Disposable.Empty;
            }));

            _Scheduler.Start();

            // This test will fail here because 6 disposables are created. 
            disposablesCreated.Should().Be(6);
            disposabledDisposed.Should().Be(6); // but is actually 5

        }

    }

当我处理订阅时,会再生成一个IDisposable,并且永远不会发送到订阅。

产生了6个一次性用品,但有一个被泄漏。这是因为我在系统中设置延迟来模拟实际系统中的调度延迟。

所以我的问题是。

是否可以编写与上述类似的 SubscribeDisposable ,不会泄漏IDisposables。

1 个答案:

答案 0 :(得分:0)

由于RX强制执行的合同,我坚信上述问题没有解决方案。所以我更新了我的代码以避免上述模式。相反,我使用。

    /// <summary>
    /// Subscribes to the observable sequence and manages the disposables 
    /// with a serial disposable. That
    /// is before the function is called again the previous disposable is disposed.
    /// </summary>

    public static IDisposable SubscribeDisposable<T>
    (this IObservable<T> o, Func<T, IDisposable> fn, Action<Exception> errHandler)
    {
        var d = new SerialDisposable();

        var s = o.Subscribe(v =>
        {
                d.Disposable = Disposable.Empty;
                d.Disposable = fn(v) ?? Disposable.Empty;
        }, onError:errHandler);

        return new CompositeDisposable(s,d);

    }

因此

IObservable<IDisposable>

从我的代码中删除,因为它似乎很危险。