たとえば、以下のようなプログラムを実行します。
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 全体に影響が及ぶので、軽率に変更するべきではない点にも注意が必要です。