简体   繁体   English

日期时间漂移 - 2小时后的奇怪问题

[英]DateTime drifting - weird issue after 2 hours

I have a thread to generate a network packet every 40 ms (25 Hz), it's an infinite loop (until told to stop), and I'm using thread.sleep. 我有一个线程每40毫秒(25赫兹)生成一个网络数据包,它是一个无限循环(直到被告知停止),我正在使用thread.sleep。

When I build the packet, one of the values is the current GPS time, using DateTime.UtcNow and adding the leap seconds. 当我构建数据包时,其中一个值是当前的GPS时间,使用DateTime.UtcNow并添加闰秒。

This works fine when I start, but it drifts with time, about 2 hours later, it's 5 seconds behind. 这在我开始时工作正常,但随着时间的推移漂移,大约2小时后,它落后了5秒。

I have a Symmetrom GPS Time Server and I'm using their software as the NTP client, and it says the cumulative drift on the PC is about 1.2 seconds (most of that I've noticed is drift while the PC is off and not syncing to NTP). 我有一个Symmetrom GPS时间服务器,我正在使用他们的软件作为NTP客户端,它说PC上的累积漂移大约是1.2秒(我注意到的大部分是在PC关闭而不同步时漂移到NTP)。

Anyone have any idea whats going wrong? 任何人都知道什么是错的? I know thread.sleep isn't perfect timing, and Windows isn't an RTOS, but the drift doesn't make sense, dropping frames would. 我知道thread.sleep不是完美的计时,而Windows不是RTOS,但漂移没有意义,丢帧会。

I can't post code due to some proprietary and ITAR issues, but I can post a rough outline: 由于一些专有和ITAR问题,我无法发布代码,但我可以发布一个粗略的大纲:

while(!abort) { 
   currentTime = DateTime.UtcNow + leapSeconds ; 
   buildPacket(currentTime); 
   stream.Write(msg, 0, sendSize); 
   //NetworkStream Thread.Sleep(40); 
}

I'm in Windows 7 and using Visual Studios 2010. 我在Windows 7并使用Visual Studios 2010。

I think this happens because the time that a while loop executes is 40 ms (your sleep) + the time necessary to execute the code that builds the packet. 我认为这是因为while循环执行的时间是40 ms(您的睡眠)+执行构建数据包的代码所需的时间。

Have you tried using a System.Threading.Timer ? 您是否尝试过使用System.Threading.Timer? This way your code will execute in a separate thread then the one that is counting your time. 这样,您的代码将在一个单独的线程中执行,然后计算您的时间。 However, I don't think the performance is good enough to keep your real time application running for long. 但是,我认为性能不足以让您的实时应用程序长时间运行。

There is probably many overhead involved, including network IO. 可能涉及许多开销,包括网络IO。 You could decouple the timing from the creation like this: 您可以将创建的时间与此类型分开:

public void Timing()
{
    // while (true) to simplify...
    // You should probably remember the last time sent and adjust the 40ms accordingly
    while (true)
    {
        SendPacketAsync(DateTime.UtcNow);
        Thread.Sleep(40);
    }
}

public Task SendPacketAsync(DateTime timing)
{
    return Task.Factory.StartNew(() => {
        var packet = ...; // use timing
        packet.Send(); // abstracted away, probably IO blocking
    });
}

The other answers are on to the problem... You have overhead on top of the fact that you are sleeping. 其他答案都是针对这个问题的......除了你正在睡觉的事实之外,你还有开销。

TPL TPL

If you operate in the world of TPL , then you can create a pretty simple solution: 如果您在TPL世界中运营,那么您可以创建一个非常简单的解决方案:

while(running)
  await Task.WhenAll(Task.Delay(40), Task.Run(()=>DoIO()));

This is a great solution because it will wait for the IO operation ( DoIO() ) in case it takes longer than 40ms. 这是一个很好的解决方案,因为它将等待IO操作( DoIO() ),以防它超过40ms。 It also avoids using Thread.Sleep() which is always ideal... 它也避免使用Thread.Sleep() ,它总是理想的......

Timer 计时器

So instead, use a timer ( System.Threading.Timer ) that will fire every 40ms. 因此,请使用每40ms触发一次的计时器( System.Threading.Timer )。 This way, you can be building and sending the packet, but the timer is also still counting. 这样,您可以构建和发送数据包,但计时器仍在计数。 The risk here is if the IO operation takes longer than 40ms, you have a race condition. 这里的风险是,如果IO操作时间超过40毫秒,那么您就有竞争条件。

NOTE 注意

40ms is an OK time to expect an accurate callback, HOWEVER, lets say you decided you needed 4ms, then the OS probably wouldn't be able to provide this kind of resolution. 40ms是一个期待准确回调的好时机,但是,假设您认为您需要4ms,那么操作系统可能无法提供这种分辨率。 You would need a real-time OS for this kind of accuracy. 您需要一个实时操作系统来实现这种准确性。

I would guess you are being bitten by two things. 我猜你有两件事被咬了。

  • The default timer resolution in modern Windows is 10 ms (although there are no guarantees about that). 现代Windows中的默认计时器分辨率为10毫秒(尽管不能保证)。 If you leave it at that you will, at best, have lots of jitter. 如果你把它留在那,你最多会有很多抖动。 You can use the multimedia API to increase the timer resolution. 您可以使用多媒体API来增加计时器分辨率。 Search MSDN for timeGetDevCaps, timeBeginPeriod and timeEndPeriod. 在MSDN上搜索timeGetDevCaps,timeBeginPeriod和timeEndPeriod。
  • You should calculate your sleep interval based on some fixed start time, rather than sleeping 40 ms on every iteration. 您应该根据一些固定的开始时间来计算您的睡眠间隔,而不是在每次迭代时睡眠40毫秒。 The code below illustrates that. 下面的代码说明了这一点。

Here is some skeletal code: 这是一些骨架代码:

static void MyFunction()
{
    //
    // Use timeGetDevCaps and timeBeginPeriod to set the system timer
    // resolution as close to 1 ms as it will let you.
    //

    var nextTime = DateTime.UtcNow;

    while (!abort)
    {
        // Send your message, preferably do it asynchronously.

        nextTime = nextTime.AddMilliseconds(40);
        var sleepInterval = nextTime - DateTime.UtcNow;

        // may want to check to make sure sleepInterval is positive.

        Thread.Sleep(sleepInterval);
    }

    //
    // Use timeEndPeriod to restore system timer resolution.
    //
}

I don't know of any .Net wrappers for the multimedia time* API functions. 我不知道有关多媒体时间* API函数的任何.Net包装器。 You will probably need to use PInvoke to call them from C#. 您可能需要使用PInvoke从C#中调用它们。

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

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