簡體   English   中英

日期時間漂移 - 2小時后的奇怪問題

[英]DateTime drifting - weird issue after 2 hours

我有一個線程每40毫秒(25赫茲)生成一個網絡數據包,它是一個無限循環(直到被告知停止),我正在使用thread.sleep。

當我構建數據包時,其中一個值是當前的GPS時間,使用DateTime.UtcNow並添加閏秒。

這在我開始時工作正常,但隨着時間的推移漂移,大約2小時后,它落后了5秒。

我有一個Symmetrom GPS時間服務器,我正在使用他們的軟件作為NTP客戶端,它說PC上的累積漂移大約是1.2秒(我注意到的大部分是在PC關閉而不同步時漂移到NTP)。

任何人都知道什么是錯的? 我知道thread.sleep不是完美的計時,而Windows不是RTOS,但漂移沒有意義,丟幀會。

由於一些專有和ITAR問題,我無法發布代碼,但我可以發布一個粗略的大綱:

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

我在Windows 7並使用Visual Studios 2010。

我認為這是因為while循環執行的時間是40 ms(您的睡眠)+執行構建數據包的代碼所需的時間。

您是否嘗試過使用System.Threading.Timer? 這樣,您的代碼將在一個單獨的線程中執行,然后計算您的時間。 但是,我認為性能不足以讓您的實時應用程序長時間運行。

可能涉及許多開銷,包括網絡IO。 您可以將創建的時間與此類型分開:

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
    });
}

其他答案都是針對這個問題的......除了你正在睡覺的事實之外,你還有開銷。

TPL

如果您在TPL世界中運營,那么您可以創建一個非常簡單的解決方案:

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

這是一個很好的解決方案,因為它將等待IO操作( DoIO() ),以防它超過40ms。 它也避免使用Thread.Sleep() ,它總是理想的......

計時器

因此,請使用每40ms觸發一次的計時器( System.Threading.Timer )。 這樣,您可以構建和發送數據包,但計時器仍在計數。 這里的風險是,如果IO操作時間超過40毫秒,那么您就有競爭條件。

注意

40ms是一個期待准確回調的好時機,但是,假設您認為您需要4ms,那么操作系統可能無法提供這種分辨率。 您需要一個實時操作系統來實現這種准確性。

我猜你有兩件事被咬了。

  • 現代Windows中的默認計時器分辨率為10毫秒(盡管不能保證)。 如果你把它留在那,你最多會有很多抖動。 您可以使用多媒體API來增加計時器分辨率。 在MSDN上搜索timeGetDevCaps,timeBeginPeriod和timeEndPeriod。
  • 您應該根據一些固定的開始時間來計算您的睡眠間隔,而不是在每次迭代時睡眠40毫秒。 下面的代碼說明了這一點。

這是一些骨架代碼:

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.
    //
}

我不知道有關多媒體時間* API函數的任何.Net包裝器。 您可能需要使用PInvoke從C#中調用它們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM