我有一种方法,可以在给定的时间向我发送完成百分比的推送更新。
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%
但是,对不起,我不知道如何做得好而优雅。
答案 0 :(得分:1)
它看起来像这样:
std::string
说明:
对于每个进度更新,
// ...
{ "char", { '\0' } },
// ...
发布最近5个进度更新的列表。 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)
运算符计算最远的时间戳和最近的时间戳之间的毫秒数,将其除以已实现的进度,然后将其乘以剩余的进度,输出剩余的毫秒。 .Timestamp()
除以1000可获得秒数。答案 1 :(得分:1)
这就是我的看法:
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>>
,其中时间戳是可观察对象将达到DateTimeOffset
(或1.0
)的估计100%
。
关键在于,它使用.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()));