たとえば、以下のようなプログラムを実行します。
Sub Main() Dim sw = New Stopwatch sw.Start() For i = 1 To 1000 ' 1ms 待ち Threading.Thread.Sleep(1) Next sw.Stop() Console.WriteLine("経過 {0} ms", sw.Elapsed.TotalMilliseconds) End Sub
1ms 待ちを 1000 回繰り返しているだけなので、期待値は 1秒(1000ms) ですがそうはなりません。
実際に実行すると、以下のように 15 秒前後かかります。
C> ConsoleApplication1 経過 15076.4839 ms
これは、システムクロックの精度がデフォルトでは 15.625 ms であるためです。
つまり、1ms 止めているつもりが、システムクロックの精度が 15.625 ms であるため、15ms 前後止まっているということです。
現在のシステムクロックの精度は Sysinternal ツールの Clockres を使うことで確認できます
https://technet.microsoft.com/ja-jp/sysinternals/bb897568.aspx
C>clockres ClockRes v2.0 - View the system clock resolution Copyright (C) 2009 Mark Russinovich SysInternals - www.sysinternals.com Maximum timer interval: 15.625 ms Minimum timer interval: 0.500 ms Current timer interval: 15.625 ms
ということで、このシステムクロックの精度を15.625 ms から 1ms に変えてやれば、冒頭のプログラムは期待通りに動いてくれそうです。
ということで、以下のプログラムによって、システムクロックの精度を 1ms に変えます。
Private Declare Sub NtSetTimerResolution Lib "ntdll.dll" Alias "NtSetTimerResolution" _ ( ByVal desiredResolution As UInt32, ByVal setResolution As Boolean, ByRef CurrentResolution As UInt32 ) Sub Main() ' 1ms : 100ns (NtSetTimerResolution の設定の分解能が 100ns であるため) Const RATIO = 10 * 1000 ' 変更値 (1ms) Dim modifyMilliseconds As Double = 1 ' 変更 Dim currentMilliseconds = 0 NtSetTimerResolution(CInt(modifyMilliseconds * RATIO), True, currentMilliseconds) ' 結果 Console.WriteLine("Current timer interval: {0}ms", currentMilliseconds / RATIO) Console.Read() End Sub
プログラムが終了してしまうとデフォルト値に戻ってしまうので、何かキーを入力しないと、プログラムの終了をしないようにしています。
上記のプログラムを実行中に、冒頭のプログラムを実行してみます。
その結果は以下。
C> clockres ClockRes v2.0 - View the system clock resolution Copyright (C) 2009 Mark Russinovich SysInternals - www.sysinternals.com Maximum timer interval: 15.625 ms Minimum timer interval: 0.500 ms Current timer interval: 1.000 ms C> ConsoleApplication1 経過 1718.0604 ms
結果は約 1.7 秒と、まだ理論値からは外れているものの、デフォルトのシステムクロック精度のときの約 15 秒よりも大幅に理論値に近くなりました。
このように、ms 単位の待ち時間やタイマー割り込みを使用する場合は、システムクロックの精度を気にする必要があります。
加えて、システムクロックの精度の変更は、そのアプリケーションだけでなく Windows 全体に影響が及ぶので、軽率に変更するべきではない点にも注意が必要です。