2014-01-13 8 views
6

私は40ms(25Hz)ごとにネットワークパケットを生成するスレッドを持っています。これは無限ループです(停止するように言われるまで)、私はthread.sleepを使用しています。DateTimeの漂流 - 2時間後の奇妙な問題

パケットを作成するとき、値の1つは現在のGPS時刻で、DateTime.UtcNowを使用して閏秒を追加します。

これは正常に動作しますが、2時間後には5秒遅れて時間とともに変動します。

私はSymmetrom GPS Time Serverを持っていて、NTPクライアントとしてソフトウェアを使用しています.PCの累積ドリフトは約1.2秒ですオフにして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でだとビジュアル・スタジオに2010

+3

あなたは 'Sleep(40)'を使っています。あなたはパケットを構築するのにかかる時間を説明しましたか? –

+0

コードを教えてください。 –

+2

私は5秒がそれほど悪くはないと言いたいと思います。 Thread.Sleep([それは本当に完璧ではありません](http://stackoverflow.com/a/1303708/2316200)の非常に小さな不正確さでさえ、時間とともに大きな間隔につながります。 Windows.Timersではさらに悪化します。約30分後に10秒後に消えます。 –

答えて

1

を使用してIこれは、whileループが実行される時間が40 ms(あなたのスリープ)+パケットを構築するコードを実行するのに必要な時間であるために起こると考えてください。

System.Threading.Timerを使用してみましたか?このようにして、あなたのコードはあなたの時間を数えているものとは別のスレッドで実行されます。しかし、私はあなたのリアルタイムアプリケーションを長く走らせるのに十分な性能はないと思います。

+2

また、スレッドが_正確にこのミリ秒で目を覚まさないので、あなたはそれを望んでいますが、OSがそのチャンスを与えたとき(それは現在のプロセス数のような多くの要因に依存します) –

0

おそらくネットワーク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 
    }); 
} 
0

他の回答問題の上にある...あなたが眠っているという事実の上にオーバーヘッドを持っています。

TPLあなたがTPLの世界で動作している場合、あなたはかなりシンプルなソリューションを作成することができます

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

それはIO操作を待つことになるので、これは素晴らしいソリューションです( DoIO())を使用してください。また、は、すべて40ミリ秒を発射しますタイマー(System.Threading.Timer)を使用し、だからではなく、

常に理想的であるThread.Sleep() ...

タイマーを使用して回避します。こうすることで、パケットを作成して送信することができますが、タイマーもカウントしています。ここでのリスクは、IO操作に40ms以上かかる場合、競合状態になることです。

NOTE

40msの正確なコールバックを期待するOKの時間である、しかし、その後、OSはおそらく解像度のこの種を提供することができない、あなたは4msのを必要と決めたと言うことができます。このような正確さのためにリアルタイムOSが必要になります。

0

私はあなたが2つの事に噛まれていると思います。

  • 最新のWindowsでは、デフォルトのタイマーの解像度は10ミリ秒です(ただし、保証はありません)。あなたがそれを残すなら、せいぜいジッタがたくさんあります。マルチメディアAPIを使用すると、タイマーの解像度を上げることができます。 timeGetDevCaps、timeBeginPeriod、およびtimeEndPeriodのMSDNを検索します。
  • すべての繰り返しでスリープするのではなく、一定の開始時間に基づいてスリープ間隔を計算する必要があります。下のコードはそれを示しています。ここで

いくつかのスケルトンコードです:

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ラッパーのか分かりません。 C#から呼び出すには、おそらくPInvokeを使う必要があります。