c#を使用して可能な限り最高の解像度のタイマーを使用したい。たとえば、11ティックごとにイベントを発生させたいと思います(ティックはPCで可能な最高のカウンターであると聞きました)。タイマーを試してみたところ、最小経過時間はミリ秒単位であることがわかりました。ストップウォッチを見ましたが、ストップウォッチはイベントを発生させません。
ありがとう。
マルチメディア タイマーを使用すると、1 秒あたり約 1000 イベントが得られます。このコードは途中で役立つはずです。
public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
/// <summary>
/// A multi media timer with millisecond precision
/// </summary>
/// <param name="msDelay">One event every msDelay milliseconds</param>
/// <param name="msResolution">Timer precision indication (lower value is more precise but resource unfriendly)</param>
/// <param name="handler">delegate to start</param>
/// <param name="userCtx">callBack data </param>
/// <param name="eventType">one event or multiple events</param>
/// <remarks>Dont forget to call timeKillEvent!</remarks>
/// <returns>0 on failure or any other value as a timer id to use for timeKillEvent</returns>
[DllImport("winmm.dll", SetLastError = true,EntryPoint="timeSetEvent")]
static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType);
/// <summary>
/// The multi media timer stop function
/// </summary>
/// <param name="uTimerID">timer id from timeSetEvent</param>
/// <remarks>This function stops the timer</remarks>
[DllImport("winmm.dll", SetLastError = true)]
static extern void timeKillEvent( UInt32 uTimerID );
これらのタイマーは、実行後に停止してください。これらはシステムに非常に負担がかかります*。すべての例外をキャッチし、イベント ハンドラーから逃がさないようにします。
*5 つ以上のタイマーを開始すると、ほとんどのシステムの速度が大幅に低下します。イベント ハンドラーで実行するコードをできるだけ少なくし、実行中のコードが 1 ミリ秒よりも高速であるか、深刻な問題に直面していることを確認します。ラベルの表示を増やすために、10 ~ 50 ティックごとにデリゲートを開始しました。
で発生する通常のスレッド切り替えでは、Thread.Sleep
1 つのスレッド スロットがコードから解放され、約 40 ミリ秒かかります。一部の NT カーネル呼び出しでスレッド切り替えの頻度を上げることもできますが、そうしないでください。
まず、ハードウェアとソフトウェアの両方に限界があるため、コンピューターで正確なタイミングを実行することは、不可能ではないにしても非常に難しいことを認識する必要があります。良いニュースは、この種の精度が必要になることはめったにないということです。10 ティックは非常に短い時間です。この間隔で CPU によって実行される作業はほとんどなく、統計的に有意になることはありません。
参考までに、Windows の時計の精度は約 10 ミリ秒です (以前のバージョンではそれよりも短くなっています)。への呼び出しでコードをラップしてDateTime.UtcNow
も、それ以上の効果はありません。
あなたの質問では、「イベントを開催したい」と話しています。問題は、特定の間隔でイベントを発生させる時間管理オブジェクトの唯一のタイプがオブジェクトであるTimer
ことです。.NET Framework の 3 つの異なる形式 ( System.Timers.Timer
、System.Threading.Timer
、およびSystem.Windows.Forms.Timer
) で利用できます。これらにはすべて、独自の使用シナリオと相対的な癖がありますが、求めているものに近い精度を保証するものはありません。それらはそのように設計されておらず、この種の精度を提供する Windows API によって公開されている同等の関数もありません。
なぜこれをやりたいのか、ベンチマークを試みているのかと尋ねた理由は、それがゲーム全体を変えるからです. .NET Framework (バージョン 2.0 以降) は、ベンチマークやパフォーマンス プロファイリングなどの状況で経過時間を正確に測定するように明示的に設計されたStopwatch
オブジェクトを提供します。はStopwatch
単純に Windows API 関数QueryPerformanceFrequency
をラップしますQueryPerformanceCounter
(これは、その使用目的に関する私の提案を確認するはずです)。フレームワークの以前のバージョンでは、このタイプの機能にアクセスするためにこれらの関数を P/Invokeする必要がありましたが、現在では便利に組み込まれてStopwatch
います。理論的には、サブマイクロ秒のタイミングを提供できます。
しかし、問題がないわけではありません。イベントを発生させないため、現在の設計がイベント処理に依存している場合は、再考する必要があります。また、完全に正確であることも保証されていません。確かに、ハードウェアの制約を考えると可能な限り最高の解像度を持っているかもしれませんが、それは必ずしもあなたが述べた要件を満たすという意味ではありません. たとえば、 と を同じプロセッサで実行する必要があるマルチ プロセッサ システムでは信頼できない場合がありStart
ますStop
。それは問題ではありませんが、そうです。また、クロック速度を上げたり下げたりできるプロセッサでは信頼性が低くなる可能性があります。そしてあえて言うなら、QueryPerformanceCounter
最新の 2 GHz 以上のプロセッサでも約 5 マイクロ秒かかります。これにより、理論上は良好と思われるサブマイクロ秒のタイミングを実際に達成することができなくなります。繰り返しになりますが、妥当なコード プロファイラーであれば、その時間は無視できると考えるでしょう。
(参照: http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/ )
さまざまなタイマー クラスは、より大きな粒度を使用します。Threading.Timer と Timers.Timer はどちらも 1/64 秒 (15.625 ミリ秒) を使用します。
あなたが参照している「ティック」が、DateTime クラス、TimeSpan クラスによって使用され、Stopwatch によって出力される 100 ナノ秒のティックである場合、あなたが求めている 11 ティックの長さは 1,100 ナノ秒、または 1.1 マイクロ秒です。私の知る限り、その解決策を提供する組み込みのタイマーはありません。本当に 1.1 マイクロ秒ごとにイベントを発生させたい場合は、「タイマー」の概念を取り除き、代わりに短い遅延の観点から考える必要があります。スレッドの優先度を高くして、イベントをループで実行します。1.1 マイクロ秒はシステム スケジューラのタイムスライスよりも小さいと思うので、Thread.Sleep() を呼び出さないでください。代わりに遅延ループを実行する必要があります。
また、あなたが求めている時間間隔は非常に短いことに注意してください。1.1 マイクロ秒は、2 GHz プロセッサでわずか 2,200 プロセッサ サイクルです。取るに足らない量ではありませんが、多くの作業を完了するのに多くの時間はかかりません。コメントで言った 1 ティックについて話している場合、それは 200 プロセッサ サイクルにすぎません。これは、数十回の数学演算を実行し、おそらく 1 つの関数を呼び出すのに十分な時間です。