.net 如何从可观测序列计算ETA?

6tqwzwtp  于 2022-12-20  发布在  .NET
关注(0)|答案(2)|浏览(128)

我有一个方法,它可以用给定任务的完成百分比来推送更新。

public Task MyMethod(IObserver<double> progress)
{
   ...
}

我的消费者这样做:

ISubject<double> progressObserver = new Subject<double>();
await MyMethod(progressObserver);

它订阅subject以监视更新:

progressObserver.Subscribe(percent => Console.WriteLine(percent));

这工作很好,但我想计算一个ETA(估计完成时间)。我知道它可以计算考虑的时间和百分比,但如何?
当然,使用Observables(System.Reactive)有一种优雅的方法,也许可以使用最后一个n百分比通知和它们之间的时间间隔来估计100%何时完成。
但是,不好意思,我不知道该怎么做得又漂亮又优雅。

jhkqcmku

jhkqcmku1#

我是这么看的:

IObservable<Timestamped<double>> estimatedCompletion =
    progressObservable
        .Timestamp()
        .Buffer(2, 1)
        .Where(x => x.Count() == 2)
        .Scan((a, b) => a.Take(1).Concat(b).Take(1).Concat(b.Skip(1)).ToList())
        .Select(x => new
        {
            current = x[1].Value,
            delta = x[1].Timestamp.Subtract(x[0].Timestamp),
        })
        .Select(x => new
        {
            x.current,
            rate = x.current / x.delta.TotalSeconds,
        })
        .Select(x => new
        {
            x.current,
            estimated = DateTimeOffset.Now.AddSeconds((1.0 - x.current) / x.rate),
        })
        .Select(x => new Timestamped<double>(x.current, x.estimated));

这产生IObservable<Timestamped<double>>,其中时间戳是可观测量将到达1.0(或100%)的估计DateTimeOffset
关键是,它使用.Buffer(2, 1).Where(x => x.Count() == 2)来生成成对的值,因为可观察值生成值,然后它使用看似复杂的.Scan((a, b) => a.Take(1).Concat(b).Take(1).Concat(b.Skip(1)).ToList())来始终生成第一个值与最新值的对。
然后,它只是简单地在一系列步骤中进行估计计算。
因为原始序列从0.0到1.0,这将最准确地反映最终时间,这只是一个估计,但如果到达那里的步骤相当一致,那么这将相当准确。
您可以使用以下代码进行测试:

var rnd = new Random();
var progressObservable = Observable.Generate(0, x => x <= 100, x => x + 1, x => x / 100.0, x => TimeSpan.FromSeconds(rnd.NextDouble()));
o4tp2gmn

o4tp2gmn2#

它可能看起来像这样:

var secondsRemaining = progressObservable
    .Timestamp()
    .Buffer(5, 1)
    .Select(l => ((l[4].Timestamp - l[0].Timestamp).TotalMilliseconds / (l[4].Value - l[0].Value)) * (100 - l[4].Value))
    .Select(msRemaining => msRemaining / 1000);
    • 说明:**

对于每个进度更新,

  • .Buffer(5, 1)发布最近5次进度更新的列表。
  • .Timestamp()将时间戳钉在每个标签上。
  • 第一个Select运算符计算最远的时间戳和最近的时间戳之间的毫秒数,除以已完成的进度,然后乘以剩余进度,输出剩余的毫秒数。
  • 最后一个Select除以1000,得到剩余秒数。

相关问题