gpt4 book ai didi

c# observable interval 跳过滴答

转载 作者:太空宇宙 更新时间:2023-11-03 15:40:38 25 4
gpt4 key购买 nike

在我的代码中,我需要有一个长时间运行的计时器,以在每分钟的第一秒启动一些例程。我尝试使用 System.Timers.Timer,但由于计时器漂移,它不是很有用。所以我从 Reactive 扩展中实现了一个计时器,它每 200 毫秒计时一次,并在例程的开头放置了一些逻辑:

IObservable<Timestamped<long>> observable = Observable.Interval(TimeSpan.FromMilliseconds(200), Scheduler.NewThread).Timestamp();
IDisposable subscription = observable.Subscribe(x => calculator.Calculate(x.Timestamp));

然后在Calculate方法中:

public void Calculate(DateTimeOffset timeElapsed)
{
if (timeElapsed.Second != 1)
{
Log.Trace("Skip calc: second != 1. {0}", timeElapsed);
return;
}
if ((timeElapsed.LocalDateTime - lastRun).TotalSeconds < 59)
{
Log.Trace("Skip calc: interval < 60sec.");
return;
}
lastRun = timeElapsed.LocalDateTime;

var longRunningTask = new Task(() => CalcRoutine(timeElapsed), token);
longRunningTask.Start();
//etc..
}

问题是,有时这个计时器会在没有任何可理解的原因的情况下跳过大约 7 个滴答。在这种特定情况下,7:57:00 中缺少 2 个最后的滴答声,整个 7:57:01 秒中缺少:

2015-05-22 07:56:59.1550|Skip calc: second != 1. 22.5.2015 7:56:59 +02:00
2015-05-22 07:56:59.3578|Skip calc: second != 1. 22.5.2015 7:56:59 +02:00
2015-05-22 07:56:59.5606|Skip calc: second != 1. 22.5.2015 7:56:59 +02:00
2015-05-22 07:56:59.7634|Skip calc: second != 1. 22.5.2015 7:56:59 +02:00
2015-05-22 07:56:59.9662|Skip calc: second != 1. 22.5.2015 7:56:59 +02:00
2015-05-22 07:57:00.1534|Skip calc: second != 1. 22.5.2015 7:57:00 +02:00
2015-05-22 07:57:00.3562|Skip calc: second != 1. 22.5.2015 7:57:00 +02:00
2015-05-22 07:57:00.5590|Skip calc: second != 1. 22.5.2015 7:57:00 +02:00
2015-05-22 07:57:02.1502|Skip calc: second != 1. 22.5.2015 7:57:02 +02:00
2015-05-22 07:57:03.3671|Skip calc: second != 1. 22.5.2015 7:57:03 +02:00

这种行为是不正常的。它在随机时间发生,每天最多发生 1 次到 3 次。那一刻的 CPU 负载是正常的,没有尖峰。磁盘也可以。我无法在我的电脑上重复它。此外,还有另一个相同应用程序的实例,它的工作量较少,但运行良好。每天午夜重新启动应用程序。

什么可能导致这个问题?

UPD:完整代码

static void Main(string[] args)
{
var calculatorReact = new Calculator();
IObservable<Timestamped<long>> observable = Observable.Interval(TimeSpan.FromMilliseconds(200)).Timestamp();
IDisposable subscription = observable.Subscribe(x => calculatorReact.Calculate(x.Timestamp));

Console.ReadLine();
}

public class Calculator
{
DateTime lastRun = DateTime.Now.AddDays(-1);
public void Calculate(DateTimeOffset timeElapsed)
{
//start calcuation on the 1st second of every minute
if (timeElapsed.Second != 1)
{
Console.WriteLine("Skip calc: second != 1. {0}", timeElapsed);
return;
}

if ((timeElapsed.LocalDateTime - lastRun).TotalSeconds < 59)
{
Console.WriteLine("Skip calc: interval < 60sec.");
return;
}
lastRun = timeElapsed.LocalDateTime;

var tokenSource = new CancellationTokenSource();
CancellationToken token = tokenSource.Token;
var longRunningTask = new Task(() => {
Console.WriteLine("Calulating..");
}, token);
longRunningTask.Start();

}
}

UPD2 问题在于该服务器上的时间同步。由于某些内部原因,我们不得不使用我们的定制软件,当它发现差异时会迅速改变系统时钟。因此它可以轻松地将时间从 7:57:00 转移到 7:57:02。

抱歉占用您的时间。

最佳答案

由于不可预测的舍入误差,使用精确匹配来匹配基于计时器的值本质上是不可靠的。这与使用 float 非常相似。你永远不应该做 x == 1.0 .你应该做的是 abs(x-1.0) < 0.00001 .通过这种方式,您可以通过引入一些公差来消除图片中的小舍入误差。

在你的情况下,我认为你可以做同样的事情:用毫秒代替秒,或者,更好的是直接用滴答,而不是将精确值与精确值相比较,引入一些公差

关于c# observable interval 跳过滴答,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30390874/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com