Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

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:

 

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?

  • Administrators
Posted

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?

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted

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:

 

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 :)

Never trouble another for what you can do for yourself.
Posted

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.

  • Administrators
Posted (edited)

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.

Edited by PlausiblyDamp

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

  • Administrators
Posted

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.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted
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".

Posted
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.
Posted

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:

 

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

Posted

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:

 

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) 

Posted

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

Posting Guidelines

 

Avatar by Lebb

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...