簡體   English   中英

如何使用HistoricalScheduler將IEnumerable轉換為IObservable

[英]How to convert IEnumerable to IObservable using HistoricalScheduler

我有一個IEnumerable<T> ,其中T允許推導相關的時間戳。

我想將其轉換為IObservable<T>但我希望使用HistoricalScheduler以便根據派生的時間戳發生通知。 這樣做允許使用內置的RX方法進行窗口化,滑動窗口等,這最終是我想要利用的。

關於如何進行此操作的許多建議建議使用Generate() 但是,此方法會導致StackOverflowExceptions 例如:

    static void Main(string[] args)
    {

        var enumerable = Enumerable.Range(0, 2000000);
        var now = DateTimeOffset.Now;
        var scheduler = new HistoricalScheduler(now);
        var observable = Observable.Generate(
            enumerable.GetEnumerator(), 
            e => e.MoveNext(), 
            e => e, 
            e => Timestamped.Create(e, now.AddTicks(e.Current)), 
            e => now.AddTicks(e.Current), 
            scheduler);
        var s2 = observable.Count().Subscribe(eventCount => Console.WriteLine("Found {0} events @ {1}", eventCount, scheduler.Now));
        scheduler.Start();
        s2.Dispose();
        Console.ReadLine();
    }

這將導致堆棧溢出。

無法使用標准的ToObservable()方法,因為盡管它允許指定自定義調度程序,但它不提供任何機制來控制如何在該調度程序上調度生成的通知。

如何將IEnumerable轉換為具有明確預定通知的IObservable

UPDATE

在以下測試中嘗試使用Asti的代碼:

    static void Main(string[] args)
    {
        var enumerable = Enumerable.Range(0, 2000000);
        var now = DateTimeOffset.Now;
        var series = enumerable.Select(i => Timestamped.Create(i, now.AddSeconds(i)));
        var ticks = Observable.Interval(TimeSpan.FromSeconds(1)).Select(i => now.AddSeconds(i));
        var scheduler = new HistoricalScheduler(now);
        Playback(series,ticks,scheduler).Subscribe(Console.WriteLine);
        scheduler.Start();
    }

但是它會拋出ArgumentOutOfRangeException

Specified argument was out of the range of valid values.
Parameter name: time

   at System.Reactive.Concurrency.VirtualTimeSchedulerBase`2.AdvanceTo(TAbsolute time)
   at System.Reactive.AnonymousSafeObserver`1.OnNext(T value)
   at System.Reactive.Linq.ObservableImpl.Select`2._.OnNext(TSource value)
   at System.Reactive.Linq.ObservableImpl.Timer.TimerImpl.Tick(Int64 count)
   at System.Reactive.Concurrency.DefaultScheduler.<>c__DisplayClass7_0`1.<SchedulePeriodic>b__1()
   at System.Reactive.Concurrency.AsyncLock.Wait(Action action)
   at System.Reactive.Concurrency.DefaultScheduler.<>c__DisplayClass7_0`1.<SchedulePeriodic>b__0()
   at System.Reactive.Concurrency.ConcurrencyAbstractionLayerImpl.PeriodicTimer.Tick(Object state)
   at System.Threading.TimerQueueTimer.CallCallbackInContext(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.TimerQueueTimer.CallCallback()
   at System.Threading.TimerQueueTimer.Fire()
   at System.Threading.TimerQueue.FireNextTimers()
   at System.Threading.TimerQueue.AppDomainTimerCallback()

我們創建一個運算符,它在歷史調度程序上重放一個有序的事件序列,並根據指定的observable移動時間。

    public static IObservable<T> Playback<T>(
        this IEnumerable<Timestamped<T>> enumerable,
        IObservable<DateTimeOffset> ticks,
        HistoricalScheduler scheduler = default(HistoricalScheduler)
    )
    {
        return Observable.Create<T>(observer =>
        {
            scheduler = scheduler ?? new HistoricalScheduler();

            //create enumerator of sequence - we're going to iterate through it manually
            var enumerator = enumerable.GetEnumerator();

            //set scheduler time for every incoming value of ticks
            var timeD = ticks.Subscribe(scheduler.AdvanceTo);

            //declare an iterator 
            Action scheduleNext = default(Action);
            scheduleNext = () =>
            {
                //move
                if (!enumerator.MoveNext())
                {
                    //no more items
                    //sequence has completed
                    observer.OnCompleted();
                    return;
                }

                //current item of enumerable sequence
                var current = enumerator.Current;

                //schedule the item to run at the timestamp specified
                scheduler.ScheduleAbsolute(current.Timestamp, () =>
                {
                    //push the value forward
                    observer.OnNext(current.Value);

                    //schedule the next item
                    scheduleNext();                        
                });
            };

            //start the process by scheduling the first item
            scheduleNext();

            //dispose the enumerator and subscription to ticks
            return new CompositeDisposable(timeD, enumerator);
        });
    }

移植你之前的例子,

   var enumerable = Enumerable.Range(0, 20000000);
            var now = DateTimeOffset.Now;
            var series = enumerable.Select(i => Timestamped.Create(i, now.AddSeconds(i)));
            var ticks = Observable.Interval(TimeSpan.FromSeconds(1)).Select(i => now.AddSeconds(i));

            series.Playback(ticks).Subscribe(Console.WriteLine);

我們通過可枚舉來讀取它以保持惰性,並使用簡單的Interval observable設置時鍾。 縮短間隔會使其播放速度更快。

菲爾,我認為無論你是否嘗試每次發出通知都會發現問題。 Rx只是無法跟上。

我看到@asti在這里做了什么,但我認為它可以使用Paul已經擁有的東西變得更簡單( IEnumerable<Timestamped<T>>

public static IObservable<T> Playback<T>(
   this IEnumerable<Timestamped<T>> enumerable,
   IScheduler scheduler)
{
    return Observable.Create<T>(observer =>
    {
        var enumerator = enumerable.GetEnumerator();

        //declare a recursive function 
        Action<Action> scheduleNext = (self) =>
        {
            //move
            if (!enumerator.MoveNext())
            {
                //no more items (or we have been disposed)
                //sequence has completed
                scheduler.Schedule(()=>observer.OnCompleted());
                return;
            }

            //current item of enumerable sequence
            var current = enumerator.Current;

            //schedule the item to run at the timestamp specified
            scheduler.Schedule(current.Timestamp, () =>
            {
                //push the value forward
                observer.OnNext(current.Value);

                //Recursively call self (via the scheduler API)
                self();
            });
        };

        //start the process by scheduling the recursive calls.
        // return the scheduled handle to allow disposal.
        var scheduledTask = scheduler.Schedule(scheduleNext);
        return StableCompositeDisposable.Create(scheduledTask, enumerator);
    });
}

這也是調度程序不可知的,因此可以與任何調度程序一起使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM