如何优化递归函数的 Reactive 实现

How to Optimize Reactive implementation of recursive function

我尝试使用 Rx 库在 C# 中创建具有动态间隔的自定义反应式计时器。因为我需要在性能和否方面都进行最优化的代码。我最终使用递归方法的行数。代码如下:

  public static IObservable<TOut> GenerateAsync<TResult, TOut>(
  Func<Task<TResult>> initialState,
  Func<TResult, bool> condition,
  Func<TResult, Task<TResult>> iterate,
  Func<TResult, TimeSpan> timeSelector,
  Func<TResult, TOut> resultSelector,
  IScheduler scheduler = null)
    {
        var s = scheduler ?? Scheduler.Default;

        return Observable.Create<TOut>(async obs =>
        {
            //You have to do your initial time delay here.
            var init = await initialState();

            //Process the result
            obs.OnNext(resultSelector(init));

            return s.Schedule(init, timeSelector(init), async (state, recurse) =>
            {
                //Check if we are done
                if (!condition(state))
                {
                    obs.OnCompleted();
                    return;
                }

                //Initiate the next request
                state = await iterate(state);

                //Process the result
                obs.OnNext(resultSelector(state));

                //Recursively schedule again
                recurse(state, timeSelector(state));

            });
        });
    }

这种方法的问题在于,由于它是递归的,因此会出现巨大的堆栈增长。我针对计时器使用情况测试了这种方法,它的内存使用量几乎翻了一番。

Link 用于测试代码,我用于获取内存使用测试。

如何在不失去其使用能力的情况下优化此功能?

这似乎是任务和 Observable 的大杂烩 - 仅 Observable 就足以表达。

回答您的基本问题 - 创建一个具有可变时间段的间隔计时器,该时间段在计时器运行时提供。这可以表示为一个 observable,它将 TimeSpan 的 observable 作为输入,returns ticks 作为输出,很像 built-in Observable.Interval.

    static IObservable<long> MutableInterval(IObservable<TimeSpan> period, IScheduler scheduler)
    {
        return period.Select(timespan => Observable.Interval(timespan, scheduler))
                     .Switch()
                     .Scan(0L, (a, _) => a + 1);
    }

这是一个周期缓慢下降的测试:

 var slowlyDecreasing =
                Observable.Interval(TimeSpan.FromSeconds(1))
                          .StartWith(0)
                          .Select(p => TimeSpan.FromMilliseconds(1000 / (p + 1)))
                          .Do(p => Console.WriteLine("Period changed to {0}", p)); 

MutableInterval(slowlyDecreasing, Scheduler.Default).Subscribe(Console.WriteLine);