簡體   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