繁体   English   中英

如何从可观察序列计算出预计到达时间?

[英]How to calculate an ETA from an Observable sequence?

我有一个方法,可以在给定的时间向我发送%完成的更新。

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%。

但是,对不起,我不知道如何做得好而优雅。

它可能看起来像这样:

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可获得秒数。

这是我的看法:

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()));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM