繁体   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