tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルtimeSetEventでタイマー処理
記事No11842
投稿日: 2017/05/31(Wed) 06:24
投稿者年金生活
既存のtimerは分解能も精度も悪いので、timeSetEventで書いてみました。
1〜50mSecで時間を計測してみた結果は以下のようになりました。
立ち上がり時がうまくいっていません。(実際は25mSec程度で使うので、実用上はオケ)
改善する方法はありますか? バグ落ちしそうなところはありませんか?
ちなみにWin7/VB2013です、VB歴ほぼなし。

1mSec: 2.57 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.85 0.99 0.90 0.99 0.98 0.98 0.98 0.98 0.99 0.98
5mSec: 5.97 0.00 3.38 4.99 5.02 4.98 4.91 5.00 5.02 5.01 4.94 4.99 4.97 4.94 5.01 5.02 4.93 5.01
10mSec: 11.73 2.52 10.00 9.97 9.95 9.99 9.96 9.98 9.98 9.99 9.96 9.95 10.00 9.98 9.98 9.99 9.97
50mSec: 51.91 42.45 49.98 49.94 49.97 49.97 49.93 49.94 49.98 49.91 49.98 49.95 50.01 49.98 49.90

=========================================
Public Class Form1
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        sw.Start()
        Call TimerStart(25)
    End Sub
End Class
===========================================
Module Module1

    Public Delegate Sub TimerProcDelegate(ByVal uID As Integer, _
        ByVal uMsg As Integer, _
        ByVal dwUser As Integer, _
        ByVal dw1 As Integer, _
        ByVal dw2 As Integer)
    Public Declare Function timeBeginPeriod Lib "winmm.dll" ( _
        ByVal uPeriod As Integer) As Integer
    Public Declare Function timeEndPeriod Lib "winmm.dll" ( _
        ByVal uPeriod As Integer) As Integer
    Public Declare Function timeSetEvent Lib "winmm.dll" ( _
        ByVal uDelay As Integer, _
        ByVal uResolution As Integer, _
        ByVal lpFunction As TimerProcDelegate, _
        ByVal dwUser As Integer, _
        ByVal uFlags As Integer) As Integer
    Public Declare Function timeKillEvent Lib "winmm.dll" ( _
        ByVal uID As Integer) As Integer

    Public gIntTimerID As Integer '高精度タイマID

    Public sw As New System.Diagnostics.Stopwatch()
    Private proc As TimerProcDelegate

    ' <<<< タイマの起動 >>>>
    Public Sub TimerStart(ByVal mSec As Integer)
        Call timeBeginPeriod(1)
        proc = AddressOf TimerProc
        gIntTimerID = timeSetEvent(mSec, 1, proc, 0, 1)
    End Sub

    ' <<<< タイマの停止 >>>>
    Public Sub TimerStop()
        Call timeKillEvent(gIntTimerID)
        Call timeEndPeriod(50)
        System.Threading.Thread.Sleep(100)
    End Sub

    ' <<<< 高精度タイマで繰り返す >>>>
    Public Sub TimerProc(ByVal uTimerID As Integer, ByVal uMsg As Integer, ByVal dwUser As Integer, ByVal dw1 As Integer, ByVal dw2 As Integer)

        sw.Stop()
        Console.Write((sw.Elapsed.ToString("fffff") / 100).ToString("0.00") & " ")
        sw.Restart()

    End Sub
End Module

[ツリー表示へ]
タイトルRe: timeSetEventでタイマー処理
記事No11843
投稿日: 2017/05/31(Wed) 17:13
投稿者魔界の仮面弁士
> 既存のtimerは分解能も精度も悪いので、timeSetEventで書いてみました。

timer といってもいろいろありますが、それは本題では無いのでさておき:

System.Windows.Forms.Timer
System.Windows.Threading.DispatcherTimer
System.Threading.Timer
System.Timers.Timer


そもそも、何のために高精度なタイマーを必要としているのでしょうか。
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=27114&forum=7



> 立ち上がり時がうまくいっていません。

そういうものです。

timeBeginPeriod および timeEndPeriod による分解能の変更は即時には行われません。
変更されるまでに幾許かの時間を要します(変更に必要な時間は一定ではありません)。

また、この設定はシステム全体に影響を与えるため、他のアプリケーションでも
分解能が変更されていた場合、その中の最高精度の値が採用されます。



> Call timeBeginPeriod(1)
> Call timeEndPeriod(50)

timeBeginPeriod と timeEndPeriod の引数には、同じ値を指定してください。



これは OK
 timeBeginPeriod(50)
 DoWork1()      '分解能50
 timeBeginPeriod(1)
 DoWork2()      '分解能1
 timeEndPeriod(1)
 DoWork3()      '分解能50
 timeEndPeriod(50)



これは OK
 timeBeginPeriod(18)
 DoWork1()      '分解能18
 timeEndPeriod(18)
 DoWork2()      '元の分解能
 timeBeginPeriod(18)
 DoWork3()      '分解能18
 timeEndPeriod(18)


これは NG … 同じ分解能でのネストはできません
 timeBeginPeriod(18)
 DoWork1()
 timeBeginPeriod(18)
 DoWork2()
 timeEndPeriod(18)
 DoWork3()
 timeEndPeriod(18)

[ツリー表示へ]
タイトルRe^2: timeSetEventでタイマー処理
記事No11844
投稿日: 2017/06/01(Thu) 04:12
投稿者年金生活
> timeBeginPeriod および timeEndPeriod による分解能の変更は即時には行われません。
> 変更されるまでに幾許かの時間を要します(変更に必要な時間は一定ではありません)。

なるほど、一回捨てれば良いと理解しましたので、以下のように変更しました。
1mSecで"0.00"が返ってこなくなり、満足のいく結果となりました。
ありがとうございました。

1mSec: 2.82 -92.66 0.95 0.98 0.97 0.99 1.02 0.96 0.98 0.99 0.98 0.99 1.00 0.97 0.99 0.99 0.99 0.99



> timeBeginPeriod と timeEndPeriod の引数には、同じ値を指定してください。

そのように変更します、ありがとうございます。

==================================================
    ' <<<< タイマの起動 >>>>
    Public Sub TimerStart(ByVal mSec As Integer)
        Call timeBeginPeriod(1)
        proc = AddressOf TimerProc

        gIntTimerID = timeSetEvent(mSec, 1, proc, 0, 0)
        System.Threading.Thread.Sleep(100)
        Console.Write("-")
        gIntTimerID = timeSetEvent(mSec, 1, proc, 0, 1)
    End Sub
=====================================================

[ツリー表示へ]
タイトルRe: timeSetEventでタイマー処理
記事No11845
投稿日: 2017/06/01(Thu) 19:26
投稿者YuO
> 既存のtimerは分解能も精度も悪いので、timeSetEventで書いてみました。
> 1〜50mSecで時間を計測してみた結果は以下のようになりました。
> 立ち上がり時がうまくいっていません。(実際は25mSec程度で使うので、実用上はオケ)
> 改善する方法はありますか? バグ落ちしそうなところはありませんか?
> ちなみにWin7/VB2013です、VB歴ほぼなし。

timeSetEventで呼ばれるコールバック関数ですが,
https://msdn.microsoft.com/en-us/library/dd757634(v=vs.85).aspx
から直接リンクは張られていないものの,
https://msdn.microsoft.com/en-us/library/dd757632(v=vs.85).aspx
を見ればわかるとおり,TimerProcというプレースホルダがあります。
そして,その関数のリファレンス
https://msdn.microsoft.com/en-us/library/dd757631(v=vs.85).aspx
には,
・PostMessage
・timeGetSystemTime
・timeGetTime
・timeSetEvent
・timeKillEvent
・midiOutShortMsg
・midiOutLongMsg
・OutputDebugString
以外のシステム定義関数 (つまりはAPI) を呼び出すな,と書いてあります。

Interop部分で.NET Frameworkが上記以外のAPIを呼び出す可能性がある以上,
根本的にtimeSetEventを.NET Frameworkで使うのは危険だと思います。

[ツリー表示へ]
タイトルRe^2: timeSetEventでタイマー処理
記事No11846
投稿日: 2017/06/02(Fri) 03:46
投稿者年金生活
> Interop部分で.NET Frameworkが上記以外のAPIを呼び出す可能性がある以上,
> 根本的にtimeSetEventを.NET Frameworkで使うのは危険だと思います。

いくつかのAPIを使っていますので、危ないですね。
他のタイマーを勉強して置き換えるようにします。
ありがとうございました。

[ツリー表示へ]