[英]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.