Handling Multithreads

Pickle

Newcomer
Joined
Mar 1, 2006
Messages
18
My goal is to wrap the multimedia functions into a class that operates similar to the timer control. I need something with a high accuracy and precision. It works but I dont think Im managing the threads right. Heres what I do:

Visual Basic:
Imports System.Threading
Imports System.Threading.Thread

Public Class CTimer
    Private Declare Function timeGetTime Lib "winmm.dll" () As Integer
    Private Declare Function timeBeginPeriod Lib "winmm.dll" (ByVal uPeriod As Integer) As Integer
    Private Declare Function timeEndPeriod Lib "winmm.dll" (ByVal uPeriod As Integer) As Integer

    Public Event TimerElapsed()

    Private CheckThread As Thread

    Private objLock As Object
    Private intInterval As Integer = 0
    Private intStartTime As Integer = 0

    Public Sub New()
        timeBeginPeriod(1)
        objLock = New Object
    End Sub

    Public Sub StartTimer(ByVal interval As Integer)
        intInterval = interval
        intStartTime = timeGetTime()

        CheckThread = New Thread(AddressOf Me.CheckTime)
        Thread.CurrentThread.Priority = ThreadPriority.Highest
        CheckThread.Start()
    End Sub

    Public Sub StopTimer()
        If Not CheckThread Is Nothing Then
            Try
                CheckThread.Abort() 'Causing a problem
                CheckThread = Nothing
            Catch ex As Exception
                MsgBox("CTimer Thread would not abort", MsgBoxStyle.Critical, "ERROR")
            End Try
        End If

        timeEndPeriod(1)
    End Sub

    Private Sub CheckTime()
        Dim start_time As Integer
        Dim interval As Integer

        SyncLock objLock
            start_time = intStartTime
            interval = intInterval
        End SyncLock

        While timeGetTime() - start_time < interval
            Sleep(1)
        End While

        RaiseEvent TimerElapsed()
    End Sub
End Class

So to summarize I start a thread that checks the time variables. Once the correct condition has been reached the event is fired. This goes to a handler that in turn starts the timer calling the StartTimer() member. This is the point where I think im going wrong. When I set CheckThread a second time, im blowing away from previous reference to the first thread? It should die on its own, but how do I know? If none of the threads die off, I could be infinitely creating new threads. Is there a better way to do this?
 
Exactly how high a precision do you need? Are we talking millisecond accuracy being good enough or something more accurate than that?

Also be aware that calling .Abort on a thread doesn't guarantee it will exit there and then - plus it throws a ThreadAbortedException you will need to handle as well.

Being honest using a high priority thread in a tight loop like you are doing though is not a good way to approach the problem as it will potentially drive the CPU @ 100% and interfere with the performance of other running applications.

Would the built in System.Timers.Timer class not be sufficient for your needs?
 
Threads

When I set CheckThread a second time, im blowing away from previous reference to the first thread? It should die on its own, but how do I know? If none of the threads die off, I could be infinitely creating new threads. Is there a better way to do this?

The thread will die when the event handler returns, as at this point it will also return from CheckTime and so terminate. Whether or not there is a reference to the thread will not affect the thread itself terminating, only whether there is a .Net Thread object associated with the thread.

If the event handler never returns, then the thread will never die, but in this case it is not your problem to worry about - it is the responsibility of the handler. In fact your StopTimer method could abort a thread executing within the event handler, which is not very polite. To counter this you should probably be setting CheckThread to Nothing before raising the event, or even better, don't use Abort at all. This would be much better:

Visual Basic:
Public Sub StopTimer()
    CheckThread = Nothing
    timeEndPeriod(1)
End Sub

Private Sub CheckTime()
    Dim start_time As Integer
    Dim interval As Integer

    SyncLock objLock
        start_time = intStartTime
        interval = intInterval
    End SyncLock

    While timeGetTime() - start_time < interval
        Sleep(1)
        'Check if StopTimer has been called, and terminate the thread if so
        If CheckThread Is Nothing Then Return
    End While

    'Disconnect thread when it enters the event handler
    CheckThread = Nothing

    RaiseEvent TimerElapsed()
End Sub

This highlights another problem with your code - timeBeginPeriod is called within the constructor, but timeEndPeriod is called within the StopTimer method. If StartTimer were invoked after StopTimer, there is no call to timeStartPeriod so the timer may not function properly. The class needs to keep track of this and either call timeStartPeriod again, or throw an InvalidOperationException if StartTimer is invoked after StopTimer.

Having said all that, I have to agree with PlausiblyDamp that using a tight busy wait loop is not an ideal timing method.

Good luck :)
 
I do need the resolution around 1 ms, as im controlling a device to generate a waveform that has specific timing requirements. I tried using the timer control, but it wasnt accurate enough.

Before you guys posted I came up with one solution. I added a list of thread and each time I create a thread I add it to the list. Each time I start the timer I look for stopped threads and remove them from the list.
When I stop the timer I then go through the list to stop all of the remaining threads.

I will look over your ideas some more to see if I can improve my class.
 
There is still a problem with your code in the fact that Sleep() doesn't have millisecond accuracy either :(

You might find the timeSetEvent function more useful as it will take care of raising events based on elapsed time and appears to have the same accuracy as the other WinMM functionality.
 
Last edited:
In fact having a rummage around the internet I came across this Project. One of the supporting classes is a timer Clicky that might give you a bit more information.

Be aware though both the project and the class in question are licensed under the LGLP and as such you would be bound by it's terms and conditions if you choose to use the source. If you find the license too restrictive or whatever then the basic idea is use the CreateTimerQueue and related functions to generate your events.
 
There is still a problem with your code in the fact that Sleep() doesn't have millisecond accuracy either :(

You might find the timeSetEvent function more useful as it will take care of raising events based on elapsed time and appears to have the same accuracy as the other WinMM functionality.

Your right about the sleep, although it hasnt seemed to interfere yet.

Im trying timSetEvent, but im having some problems with the Declare, what should the callback parameter be? Its a long, but I want to give the function my "AddressOf MyFunction".
 
Scrap the timeSetEvent, the queue does look better. I wont be using anything below W2K so thats the one I think I will try. Getting the correct declare statements is challenge though.
 
Ok i found an VB6 example at http://www.ex-designz.net/apidetail.asp?api_id=82

Which ive converted to VB.NET, it seems to work
Heres the converted code:

Visual Basic:
Public Class CQueueTimer
    Delegate Sub SubClassProcDelegate()

    Private Declare Function CreateTimerQueue Lib "kernel32.dll" () As Integer
    Private Declare Function CreateTimerQueueTimer Lib "kernel32.dll" (ByRef phNewTimer As Integer, _
                                                                       ByVal TimerQueue As Integer, _
                                                                       ByVal Callback As SubClassProcDelegate, _
                                                                       ByVal Parameter As Integer, _
                                                                       ByVal DueTime As Integer, _
                                                                       ByVal Period As Integer, _
                                                                       ByVal Flags As Integer) As Integer
    Private Declare Function DeleteTimerQueue Lib "kernel32.dll" (ByVal TimerQueue As Integer) As Integer
    Private Declare Function DeleteTimerQueueTimer Lib "kernel32.dll" (ByVal TimerQueue As Integer, _
                                                                       ByVal Timer_Renamed As Integer, _
                                                                       ByVal CompletionEvent As Integer) As Integer
    Private hQueue As Integer
    Private hTimer As Integer

    Private TimerCallBack As SubClassProcDelegate

    Public Sub New(ByVal callback As SubClassProcDelegate)
        TimerCallBack = callback
        hQueue = CreateTimerQueue()
    End Sub

    Public Sub CloseTimer()
        DeleteTimerQueue(hQueue)
    End Sub

    Public Sub StartTimer(ByVal time As Integer)
        If hTimer = 0 Then
            CreateTimerQueueTimer(hTimer, hQueue, TimerCallBack, 0, time, 0, 0)
        End If
    End Sub

    Public Sub StopTimer()
        If hTimer <> 0 Then
            DeleteTimerQueueTimer(hQueue, hTimer, 0)
            hTimer = 0
        End If
    End Sub
End Class
 
Yet another reply....

I had to do a small update, my app was crashing when more than 1 timer was active, setting the flag EXECUTEINTIMERTHREAD fixed it. Heres the code:

Visual Basic:
Private WT_EXECUTEDEFAULT As Long = &H0             
Private WT_EXECUTEINTIMERTHREAD As Long = &H20      
Private WT_EXECUTEINIOTHREAD As Long = &H1          
Private WT_EXECUTEINPERSISTENTTHREAD As Long = &H80 
Private WT_EXECUTELONGFUNCTION As Long = &H10       
Private WT_EXECUTEONLYONCE As Long = &H8            
Private WT_TRANSFER_IMPERSONATION As Long = &H100  

CreateTimerQueueTimer(hTimer, hQueue, TimerCallBack, 0, time, 0, WT_EXECUTEINTIMERTHREAD)
 
Hey Pickle,

I don't have much to add here because I'm not very strong with threading. But just to be sure, I assume you tried the 'System.Timers.Timer' or 'System.Threading.Timer', as opposed to the 'System.Windows.Timer', I assume? The 'System.Windows.Timer' has to be innacurate because it operates through the massage pump... The others should definately be more accurate -- but I still don't know if they'll meet your 1ms accuracy requirement.

Just a thought... Ignore all this if you've already covered this base. :)

Mike
 
Back
Top