[英]C# Rx How to properly dispose of source Enumerable in created Observable
我想将一个IEnumerable,IDisposable(源)改编成一个Observable,并希望知道这样做的最佳方法,并在取消订阅时调用source.Dispose方法。
在introtorx.com上有一个关于调整IEnumerable的例子 ,但它明确指出它有许多缺点,例如错误的处理模式,糟糕的并发模型,没有错误处理等等......内置版本处理这些缺点。 但内置版本似乎没有在取消订阅时调用源IEnumerable上的Dispose。
理想情况下,我想使用.Publish().RefCount()
模式在同一个源上拥有多个订阅者,并且只有在取消订阅时才调用源Dispose()
。
这是我尝试的代码,虽然它不起作用。
static void FromEnumerableTest() {
var observable = Observable.Create<int>(
observer => {
var source = new JunkEnumerable();
foreach (int i in source) {
observer.OnNext(i);
}
return () => {
source.Dispose();
};
})
.SubscribeOn(Scheduler.Default)
.Do(i => Console.WriteLine("Publishing {0}", i)) // side effect to show it is running
.Publish()
.RefCount();
//var observable = Observable.ToObservable(new JunkEnumerable())
// .SubscribeOn(Scheduler.Default)
// .Do(i => Console.WriteLine("Publishing {0}", i)) // side effect to show it is running
// .Publish()
// .RefCount();
Console.WriteLine("Press any key to subscribe");
Console.ReadKey();
var subscription = observable.Subscribe(i => Console.WriteLine("subscription : {0}", i));
Console.WriteLine("Press any key to unsubscribe");
Console.ReadKey();
subscription.Dispose();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
class JunkEnumerable : IEnumerable<int>, IDisposable {
public void Dispose() { Console.WriteLine("JunkEnumerable.Dispose invoked"); }
public IEnumerator<int> GetEnumerator() { return new Enumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
class Enumerator : IEnumerator<int> {
private int counter = 0;
public int Current {
get {
Thread.Sleep(1000);
return counter++;
}
}
object IEnumerator.Current { get { return Current; } }
public void Dispose() { Console.WriteLine("JunkEnumerable.Enumerator.Dispose invoked"); }
public bool MoveNext() { return true; }
public void Reset() { counter = 0; }
}
}
Rx订阅生命周期分为三个阶段:
如果订阅永远不会完成,则不会发生取消订阅代码。 毕竟,如果您从未完全订阅,为什么还需要取消订阅? 您的示例代码在订阅代码中具有无限循环,因此它永远不会完成,因此取消订阅代码永远不会发生。
处理IDisposable
的常规方法是使用Observable.Using
。 处理IEnumerable
的常规方法是使用.ToObservable
。 如果您尝试将异步引入同步,可枚举的代码(如您的示例),则可以按如下方式执行:
var observable = Observable.Using(() => new JunkEnumerable(), junk =>
Observable.Generate(junk.GetEnumerator(), e => e.MoveNext(), e => e, e => e.Current, e => TimeSpan.FromMilliseconds(20))
);
只要TimeSpan大于15毫秒,Rx就会将其变为异步,完成订阅。 随后的值是观察阶段的一部分,并且将完全取消订阅。
这是一个在指定的调度程序上运行枚举的运算符。 我们安排可枚举的每个枚举,以便一次性用品可以正确返回。
public static IObservable<T> ToObservableOn<T>(this IEnumerable<T> source, IScheduler scheduler = default(IScheduler))
{
scheduler = scheduler ?? Scheduler.Default;
return Observable.Create<T>(
(observer) =>
{
var disposed = new BooleanDisposable();
var enumerator = source.GetEnumerator();
Action scheduleNext = default(Action);
scheduleNext = () =>
{
if (disposed.IsDisposed)
return;
if (!enumerator.MoveNext())
{
observer.OnCompleted();
return;
}
observer.OnNext(enumerator.Current);
scheduler.Schedule(scheduleNext);
};
scheduler.Schedule(scheduleNext);
return StableCompositeDisposable.Create(disposed, enumerator);
});
}
从您的示例中,我们只需将SubscribeOn
更改为:
var observable =
new JunkEnumerable()
.ToObservableOn(Scheduler.Default)
.Do(i => Console.WriteLine("Publishing {0}", i)) // side effect to show it is running
.Publish()
.RefCount();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.