c#可观察的间隔跳过刻度
本文关键字:观察 | 更新日期: 2023-09-27 17:50:19
在我的代码中,我需要有一个长运行计时器来启动一些例程的每一分钟的第一秒。我试过使用System.Timers。定时器,但它不是很有用,因为定时器漂移。所以我从响应扩展中实现了一个计时器,它每200ms滴答一次,并在例程的开头添加了一些逻辑:
IObservable<Timestamped<long>> observable = Observable.Interval(TimeSpan.FromMilliseconds(200), Scheduler.NewThread).Timestamp();
IDisposable subscription = observable.Subscribe(x => calculator.Calculate(x.Timestamp));
然后在计算方法中:
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
这种行为是不规律的。它会随机发生,一天一次到三次。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。
对不起,占用您的时间了。使用精确匹配匹配基于计时器的值本质上是不可靠的,因为不可预测的舍入误差。这与处理浮点数非常相似。你不应该做x == 1.0
。你应该做的是abs(x-1.0) < 0.00001
。这样,你就可以通过引入一些公差来消除小的舍入误差。
在你的情况下,我认为你可以做同样的事情:不是用秒来工作,而是用毫秒来工作,或者,更好的是直接用刻度来工作,而不是与精确的值进行比较,引入一些公差