我的生产代码中有一个错误,我跟踪并设法创建一个重现的测试用例。我正在创作
{
"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。
答案 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>
从我的代码中删除,因为它似乎很危险。