N ミリ秒ごとに (合理的に) 正確にアクションを実行するにはどうすればよいでしょうか?
-
22-09-2019 - |
質問
NTP クライアントを使用してインターネット時刻と同期するマシンがあるため、システム クロックはかなり正確であるはずです。
リアルタイムでデータを記録し、処理して渡すアプリケーションを開発しています。ここでやりたいのは、システムクロックに合わせて N ミリ秒ごとにデータを出力することです。たとえば、20ms 間隔で実行したい場合、出力は次のようになります。
13:15:05:000
13:15:05:020
13:15:05:040
13:15:05:060
stopwatch クラスの使用に関する提案を見てきましたが、それは特定のタイムスタンプを検索するのではなく、タイムスパンを測定するだけです。これを行うコードは独自のスレッドで実行されているため、比較的ブロック的な呼び出しを行う必要がある場合には問題になるはずです。
これを妥当な精度 (1 ミリ秒に近いかそれ以上の精度が望ましい) に達成する方法に関する提案があれば、大変感謝いたします。
解決
C++/CLR とどの程度うまく連携するかはわかりませんが、おそらく次のことを確認するとよいでしょう。 マルチメディアタイマー,
Windows はリアルタイムではありませんが、限りなくリアルタイムに近いものです
他のヒント
あなたはtimeGetTime()のうちのかなり正確なタイムスタンプを取得することができます。あなただけの、その戻り値は、クロック時刻に変換取得するためにいくつかの作業が必要になります。このサンプルC#コードは、アプローチを示しています。
using System;
using System.Runtime.InteropServices;
class Program {
static void Main(string[] args) {
timeBeginPeriod(1);
uint tick0 = timeGetTime();
var startDate = DateTime.Now;
uint tick1 = tick0;
for (int ix = 0; ix < 20; ++ix) {
uint tick2 = 0;
do { // Burn 20 msec
tick2 = timeGetTime();
} while (tick2 - tick1 < 20);
var currDate = startDate.Add(new TimeSpan((tick2 - tick0) * 10000));
Console.WriteLine(currDate.ToString("HH:mm:ss:ffff"));
tick1 = tick2;
}
timeEndPeriod(1);
Console.ReadLine();
}
[DllImport("winmm.dll")]
private static extern int timeBeginPeriod(int period);
[DllImport("winmm.dll")]
private static extern int timeEndPeriod(int period);
[DllImport("winmm.dll")]
private static extern uint timeGetTime();
}
考え直しでは、これはただの測定です。定期的に実行されるアクションを取得するには、あなたがtimeSetEventを使用する必要があります()。限り、あなたはtimeBeginPeriod()を使用すると、あなたはかなり近い1ミリ秒へのコールバック期間を得ることができます。一つ微妙には、前のコールバックが遅く何らかの理由だったとき、それは自動的に補償するということです。
最善の策は、インライン アセンブリを使用し、このコードのチャンクをデバイス ドライバーとして記述することです。
その方法:
- 命令数を制御できます
- アプリケーションには実行の優先順位が与えられます
最終的には、オペレーティング システムは他のプロセスからの実行要求を受け入れる必要があるため、希望する内容を保証することはできません。つまり、プロセスを実行したいと思った瞬間に、他の何かが常にビジー状態になる可能性があります。ただし、次を使用して問題を改善できます timeBeginPeriod
プロセスがタイムリーに切り替えられる可能性を高めるため、そしておそらく反復間の待機方法を巧妙に行う必要があります。常にではありませんが、ほとんどの時間スリープし、残りの時間はビジー ループを使用します。
二つのスレッドでこれをやってみてください。 1つのスレッド、こののような使用何かでループ内で高精度のタイマーを照会します。あなたはタイムスタンプを検出するとそれに整列する(または合理的に近いです)20msの境界、使用するタイムスタンプと一緒にログ出力スレッドに信号を送ります。あなたのログ出力スレッドは単にそれから渡されたタイムスタンプと出力に必要とされるものは何でもをつかむ、信号を待つでしょう。別のスレッドで2は、あなたのログ出力スレッドが(これは基本的に私は組み込みプラットフォーム上でそれを行うだろう方法だろうハードウェアタイマ割り込みをエミュレートしている)、タイマーに干渉しないことを確認します保ちます。
CreateWaitableTimer / SetWaitableTimerと、優先度の高いスレッドが約1ミリ秒の精度でなければなりません。あなたの例の出力のミリ秒のフィールドは4桁の数字を持っている理由は、私は、最大値は999(1000年以降のミリ秒= 1秒)で、知りません。
あなたが言ったように、これは完璧である必要はありませんので、行うことができますいくつかのことがあります。
私の知る限りでは、特定の時間と同期するという、タイマーが存在しません。つまり、あなたの次の時間を計算し、その特定の時間のタイマーをスケジュールする必要があります。あなたのタイマーが唯一のデルタをサポートしている場合は、それが容易に計算されていますが、簡単に時間の間CPUをキックオフする可能性があるため、あなたのデルタとタイマーがカーネルに入力された時間を計算し、よりエラーを追加します。
すでに指摘したように、は、Windowsは、リアルタイムOSではありません。 「:0010」あなたはで降りたためにタイマーをスケジュールする場合でも、あなたはそれを想定しなければならないので、あなたのコードでも同様にその時間後まで実行されない場合があります(たとえば、「:0540」)。限り、あなたは適切にこれらの問題を扱うように、物事が「OK」になります。
20msのは、およそWindows上のタイムスライスの長さです。ウィンドウ内の1msの種類のタイミングをヒット確実にすることなく、RTのいくつかの並べ替えは、インタイムのように追加する方法はありません。窓適切では私はあなたのオプションがのWaitForSingleObject、SleepEx、およびビジーループだと思います。