AeroForce 64 12-28-2007, 07:35 AM Hello:
I am working with a timer control and i have seen thant if i use a counter in a timer control with interval 1 and wait when it reaches 1000, there hasnt really passed a second... How can i make the timer exact??
Because if we consider that the timer interval is in milliseconds, when the counter reaches 1000, there should have passed 1 second, but if you compare it with a clock, it is not really a second...
Here's the code:
Private Sub Form_Load()
Timer1.Interval = 1
Label1.Caption = 0
End Sub
Private Sub Timer1_Timer()
Label1.Caption = Label1.Caption + 1
End Sub
How can i make the milliseconds exact?
Thanks..
Robert Collins 12-28-2007, 07:57 AM How do you know your clock is accurate?
Well, you're probably right though. The timer is probably extremely close but perhaps not exactly right on the millisecond of a second, but then clocks are probably not accurate either.
PianoMan 12-28-2007, 08:15 AM You can use the GetTickCount API to be more exact. A Timer is not the best control to use if exact accuracy is required.
the master 12-28-2007, 08:21 AM Timers arnt very good at all. You should only use them if you want something to happen every few seconds and your not bothered about precision. Anything below about 50ms doesnt work properly depending on your PC.
And yes, GetTickCount is definately the way to go. It can be used in a loop to get the same results as a timer but more precise
AeroForce 64 12-28-2007, 08:21 AM i did this:
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Sub Command1_Click()
Timer1.Enabled = True
start = GetTickCount 'the beggining
End Sub
Private Sub Timer1_Timer()
'Timer1 interval is 1
Label2.Caption = (GetTickCount - start) 'now knowing the beggining, we can start from 0
End Sub
Working nice, thanks for the idea PianoMan.
Robert Collins 12-28-2007, 09:30 AM When you enter the Timer Event you should always disable the timer, do what you need to do code-wise, and then enable the timer on exit from event.
dilettante 12-28-2007, 09:43 AM The Timer control was never meant for "timing" as such. If precise timing is important there are alternatives, but Windows isn't meant to be a real-time OS anyway.
The purpose of a Timer control is really to act as a delayed trigger or a periodic event generator for managing user interface updating and polling for status changes and such. Precision was never a goal.
It's like complaining that an egg timer is off a bit and fiddling with the amount of sand in it to compensate. GetTickCount has its own limitations.
Precision is not the same as accuracy (http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx)
Cerian Knight 12-28-2007, 09:12 PM The granularity of the Timer event is related to the OS that it is run on and number of processors. Post #5 here has a link to that info: http://www.xtremevbtalk.com/showthread.php?t=279822
be sure to check all the links in the thread, though.
passel 12-29-2007, 07:23 PM When you enter the Timer Event you should always disable the timer, do what you need to do code-wise, and then enable the timer on exit from event.
There really is no reason to disable the Timer Event inside the event.
You can't get another Timer Event while you're in the the current Timer Event and the time spent in the Event is not included in the interval.
That is, if you set the interval to 500 (ms), then there will be a minimum of 500ms (a half a second) from the time you exit the event until the next time it is triggered. If you spent 100 milliseconds in the timer event, the timer won't be firing every 500 ms, but at 100 + 500 ms.
The following code demonstrates this. The timer is set to 500 which means you have 1/2 second "between" events.
The code in the timer will take 1 second to execute.
The Debug.Print statements will show that there is .5 seconds between the end of one event and the begining of the next, but that there are 1.5 seconds between the beginning of one event and the beginning of the next.
Notice that the timer is not disabled in the event and we are not getting reentrant events. If we were, the TimeOut variable would always be 0, giving us a bad debug.print (not .5) and we would be filling up the process stack.
Private Sub Form_Load()
Timer1.Interval = 500
End Sub
Private Sub Timer1_Timer()
Static TimeOut As Single
Static TimeIn As Single
Dim sDelay As Single
'Print the time between the exit of the event and the entry of the next event
' and the total time between envent entries
Debug.Print Timer - TimeOut, Timer - TimeIn
TimeIn = Timer 'Save the time we came in to the event
sDelay = Timer + 1 'Delay inside the event for one second
While Timer < sDelay
DoEvents
Wend
TimeOut = Timer 'Save the time we exit, to use in our print
End Sub
This is true for the VB Timer Control.
I think it is also basically true for a timer that you may create manually through the Windows API. I believe that for standard Windows timers, the timer is essentially a one shot and is automatically disabled when you get the event. You have to reenable the timer before you exit the event routine. I could be wrong.
If on the other hand, you had some hardware based realtime clock card generating interrupts, you may have to follow Robert's recommendation to avoid a reentrant error condition.
dilettante 12-29-2007, 07:44 PM Even then one doesn't need to worry about re-entrancy unless foolish enough to call DoEvents() in the Timer event handler.
the master 12-29-2007, 07:46 PM Even then one doesn't need to worry about re-entrancy unless foolish enough to call DoEvents() in the Timer event handler.
Hehe. I think thats one of my problems solved!
Robert Collins 12-29-2007, 08:06 PM Disabling/Enabling the timer in the Timer Event has always been a practice of mine almost from day one. I have never heard anyone ever tell me it wasn't necessary (or that it was). Somewhere back in time for some reason I just got into the habit of doing it and never questioned it because doing so never caused me any problems even if I had a DoEvent in the Timer Event.
the master 12-30-2007, 05:45 AM I always try to avoid timers but when i do use them i never disable/enable them in the event itself. I did try it once because a had problems where i thought it was running again before the last one had ended. I was calling doevents though (because i had to)
I think ill try some other method though
dilettante 12-30-2007, 09:48 AM Timers often get used as a one-shot. Then you have no choice: disable in the handler or have the Timer fire repeatedly!
the master 12-30-2007, 09:51 AM That was a repeating event though
passel 12-31-2007, 07:53 PM Even then one doesn't need to worry about re-entrancy unless foolish enough to call DoEvents() in the Timer event handler.
But my example does call DoEvents in the Timer event handler to prove that you won't get a re-entrancy problem. I'm pretty sure I've read (and tested) that at least for VB 6.0 and earlier, that you can not get another timer event for a given timer control while in that timer's timer event.
The example code was written to show this.
On my slow machine, in the IDE, I'm calling DoEvents around 300,000 times per second within the Timer Event, and we don't get another entry into the event.
The timer is not counting down while we are in the timer event, so we won't get another entry.
You could be processing code with DoEvents in the Timer indefinitely and the timer will never trigger again unless you leave the event.
Once you leave the event, then the interval is applied and it will be at least that long before the next event is triggered.
Disabling the timer is probably a good programming practice, in that it is logical to "turn off interupts" while processing an event.
My original comment may be taken as more of a bit of trivia info about the current state of the way the VB timer control works.
Disabling the timer is a logical precaution, but actually unecessary.
the master 12-31-2007, 07:56 PM If you do disable the timer in the timer event do you need to call doevents after it for it to make any difference? I know its not needed but if you did disable it would doevents be needed?
passel 12-31-2007, 08:25 PM I always try to avoid timers but when i do use them i never disable/enable them in the event itself. I did try it once because a had problems where i thought it was running again before the last one had ended. I was calling doevents though (because i had to)
I think ill try some other method though
Using multiple timers can be tricky, and is generally not recommended.
There is no scheduling priority for multiple timers.
Set several timers to fire at the same interval and you might assume they will all execute the same number of times over a given period, but that is not true. Some will fire more often than others, and the order they are triggered is unpredictable.
If you have multiple timers, and you do a DoEvents in one because you have a long process and need to keep the user interface active, another timer will fire when it's time is due, and the code will run in that timer event, suspending the one in the original timer until the second timer is complete.
VB is a single threaded application. Through the use of DoEvents you can allow code to suspend itself temporarily, essentially pushing its address on the stack, and processing other code based on pending events. But being single threaded, the suspended code won't execute again until all the pending events are processed.
For instance, lets say I have four timers to do four different involved tasks periodically. They have DoEvents in their loop to allow the user interface to remain active as they process. One might assume that if all four timers happend to be processing at the same time, that at each DoEvent processing may be switched from one timer control to another to give apparent parallel processing by "switching threads", but this is not the case.
The actual scenario is along these lines.
One of the timers fires and starts its involved process with DoEvents.
At one of the DoEvents, a second timer fires and starts its process. The first timers code is suspended at the DoEvents call.
At one of the second timer's DoEvents, the third timer is triggered, the second suspended.
At one of the third timer's DoEvents, the fourth timer is triggered, the third suspended.
The forth timer will run to completion, the other three timers suspended until it finishes. There is no capability in VB to have the code in the other three timers be switched to and run in concert with the forth.
Once the forth timer finishes, processing will return to the third timer.
If the third timer finishes, it will return to the second.
But, if the third timer's process is long enough, and the fourth timer's interval is short enough, the fourth timer may be triggered again, suspending the third.
Once the fourth completes, it will return to the third. Eventually, the third will finish and return to the second, but then either the third or fourth may trigger during the second's processing suspending it.
It is quite possible that the first may never finish its first event if the other timers fire often enough.
Multiple timers is almost never good.
the master 01-01-2008, 05:33 AM I dont think you could get an explanation better than that. I did have that problem ages ago with loops instead. Same thing though. A second loop will pause the first loop. The trick is to have just one loop do everything
I think that problem i had with the timer was because it was running a line of code and half way through i clicked a button which disables the timer. The timer would ofcourse finish the processing that was already happening and should stop there but it seemed to be running once more after the button was clicked. I dont think thats possible so it must have been some other code causing the problem.
Im in the process of re-writing that app now anyways.
Heres a thought. Can you keep the UI active without using doevents? What if we just want our app to be active and not bother what else is happening in windows because doevents takes a long time to execute
Rockoon 01-01-2008, 07:33 AM The example code was written to show this.
On my slow machine, in the IDE, I'm calling DoEvents around 300,000 times per second within the Timer Event, and we don't get another entry into the event.
What if we just want our app to be active and not bother what else is happening in windows because doevents takes a long time to execute
How many times per second are you trying to call DoEvents() for you to have determined that it is slow?
Anything over about 20 times per second will give you a very responsive GUI, and with that in mind..
If GetTickCount() <> lastTickCount Then
lastTickCount = GetTickCount()
DoEvents()
End If
(although I have been favoring TimeGetTime() over GetTickCount() these days)
DoEvents() has a bad wrap because it makes certain event-based programming scenarios unmanagable, turning them into tedious event spaghetti.
the master 01-01-2008, 09:10 AM Doevents takes a long time to run. Yes i know its in milliseconds but it slows processing down a lot. It depends what im doing but sometimes i only call it after processing a certain amount of data rather than after each row of processing. I do have cases though where it needs to be run every time like when drawing something to a form. Adding doevents slows the processing down noticably but it will make sure the thing is drawn and visible
Rockoon 01-01-2008, 10:11 AM I'm not sure that you are understanding this..
..there is no reason to 'make sure the thing is drawn and visible' more than ~20 times per second. Certainly anything over the refresh rate of the display (often 75hz these days) is a complete waste.
I would suggest that any strategy that truely needs the primary display updated extremely often is incorrectly using the primary display as some sort of data buffer.
It will only 'slow down processing a lot' if you are calling DoEvents() excessively.. as in millions of times per second in a compiled executable.
the master 01-01-2008, 10:18 AM I use do-loops for pausing my program sometimes while allowing everything else to keep working
dim m as long
m=gettickcount
do while gettickcount<m+5000
doevents
loop
That gives a 5 second pause. Should i put a little extra code in there so doevents isnt called so often or is it pointless in this case?
Rockoon 01-01-2008, 10:22 AM I would specifically call sleep() within that loop with a reasonable sleep time.. where reasonable is again about 1/20th of a second (1000 / 20 = 50ms) .. there isnt much reason to check for events more often than that.
Edited to add:
Infact, the timer control (and gettickcount()) will tick() no faster than 55ms on older OS's.. with 15ms, 10ms, and 5ms being other common values.
the master 01-01-2008, 10:26 AM Thats good. I never thought of that. It would still need doevents thought wouldnt it? Just that doevents isnt called very often.
For a project im working on at the moment i think i can use that but i will need a little extra code to top a pause happening towards the end and causing it to be a few ms out (thats quite important in this app)
Cerian Knight 01-01-2008, 10:26 AM I agree with Rockoon. If you are unwilling to take the time to schedule the execution of DoEvents, you could use a lightweight API version of DoEvents:
http://www.xtremevbtalk.com/showthread.php?t=213459
...but it seemed to be running once more after the button was clicked. I dont think thats possible...
If you set '.Interval = 0', that is exactly what will happen. Set '.Enabled = False' will stop further events immediately.
the master 01-01-2008, 10:29 AM If you set '.Interval = 0', that is exactly what will happen.
I never set the interval to 0. It can be misleading
the master 01-01-2008, 10:33 AM That API_Doevents looks good. It was mentioned earlier that calling doevents will do eveny pending event before allowing your app to continue. Can this API_Events be used to let some pending events run but allow your app to continue before all of them have run?
dilettante 01-01-2008, 10:33 AM DoEvents() has a bad wrap because it makes certain event-based programming scenarios unmanagable, turning them into tedious event spaghetti.
DoEvents() calls are evil, but a sometimes necessary evil.
As I recall the operation goes something like:
Relinquish the processor - basically a Sleep(0) call.
Iterate each open Form.
Call the Message Loop on each modeless Form unless there is a modal form shown (then just call his loop) until the window message queues are empty.
Return with the open Form count.
It isn't a cheap process, and it can lead to re-entrancy if invoked in an event handler, which can lead to stack overflows or at best data and state corruption. However Microsoft even advises it in .Net instead of cumbersome and even more error-prone threading in most scenarios where you'd use it, typically attempting to keep the UI responsive during any long running straight-line processing.
I hesitate to think about the kind of havoc hooking the message loop or having active callbacks adds to the mix.
The best workaround leads to some non-intuitive code: Get rid of DoEvents() calls and unroll your long-running processing. "Pump" this code using a Timer control, putting each quantum of your long-running workload into the Timer event hander. If necessary make the Timer's Interval very short and treat it as a one-shot (disable it on entry to the event handler, re-enable it just prior to exit).
An alternative might be to keep the DoEvents() call but associate a counter with it, only calling DoEvents() each time the counter reaches its limit. This reduces the performance impact, but does nothing about the other dangerous side-effects of a DoEvents() call.
I haven't seen anything that guarantees a Timer event can't be reentered.
Aren't you supposed to call Me.Refresh on a Form to force painting?
the master 01-01-2008, 10:37 AM Aren't you supposed to call Me.Refresh on a Form to force painting?
Does that make the form repaint even without a doevents or some kind of break in the loop?
dilettante 01-01-2008, 10:46 AM Under Refresh the manual says:
Generally, painting a form or control is handled automatically while no events are occurring. However, there may be situations where you want the form or control updated immediately.
It says little more, but the implication is that yes, it does the same thing as letting your program relax into message loop processing or forcing it via DoEvents(). You'd have to try it to see what the actual impact is though.
That's for the form. Individual controls would need individual Refresh calls.
Rockoon 01-01-2008, 10:54 AM Yes most those problems with DoEvents() relates specifically to the 'event spaghetti' I mentioned
All your event handlers must have guards in place to ensure that they cannot run when they shouldn't...
For instance if one event (botton, perhaps) sorts a big list with added doevents calls for responsiveness and another adds/edits that list, then you would need to block the add/edit events while the sort is in progress and so forth..
Meanwhile the sorting procedure would probably need to check for an abort/exit flag triggered by a quit event and so forth...
Many of the side effects of calling doevents() wont be obvious/intuitive.. program structure will not help keep it organized.. its spaghetti!
But really this isnt much worse than using multiple threads.. as the mutex stuff and global flags can be just as spaghetti-like.
I do like the idea of leveraging the message handling system to good effect.. an initial event is raised which triggers some long operation which is broken up into smaller pieces.. each piece is triggered by an event.. piece(1) posts an event to start piece(2) when it is finished, piece(2) posts an event to start piece(3) .., and so forth
..but you still have spaghetti!
That is the crux of event-driven programming tho.. it is by necessity very much like a finite state machine, with spaghetti-like conditions and flags when necessary.
dilettante 01-01-2008, 02:07 PM I suppose if one were clever enough it'd be possible to devise a custom event and send a message to your own window from the end of the event handler itself. Ideally one would want this to queue behind other UI, etc. events that had occurred while processing the current workload quantum.
Using a one-shot Timer at "1ms." is just a quick and dirty alternative. The real trick either way is trying to tune the size of the workload chunk.
I agree about the rat's nest of difficult to follow flow and state management though. You'd almost want to wrap it all in a Class or UserControl (easy to locate the right hWnd from a UserControl) just to isolate the "spaghetti" and state data from other Form logic. Back in the Form just have one event, and have its handler call a WorkQuantum() method on the object. Maybe have a second (conventional) WorkDone event?
Hmm, come to think of it maybe one could simply raise a conventional event at the end of WorkQuantum() ???
Cerian Knight 01-01-2008, 08:36 PM Can this API_Events be used to let some pending events run but allow your app to continue before all of them have run?
Pass an hWnd (e.g. Me.hWnd) instead of '0' to the function to process only the events from the specified form.
AstroTux 01-01-2008, 08:46 PM Hi,
I try and avoid the use of Timers where possible, but if you need to use one out of convenience, and accuracy is not an issue, then note carefully that the first time a timer is started and triggered, IT IS NOT ACCURATE AT ALL!
I've found that I need to set my timer code to ignore an initialize flag that I set, so the code in it doesn't actually run. At run-time, I set the flag, fire all the timers as necessary, then reset the flag so the code is "live" for when it actually needs to be used.
If you really need accuracy, set up an interrupt (but not sure if this is possible in VB6???). I've found the system clock will slow down or speed up depending on what is executing and for how long. Its behavior is rather curious.
Best regards,
AstroTux.
the master 01-01-2008, 11:20 PM Pass an hWnd (e.g. Me.hWnd) instead of '0' to the function to process only the events from the specified form.
That will be really useful:D
Ive used some code before when i made a windows service which has an API. I think i was WaitForMultipleObjects which seems to pause the program same as Sleep() but it doesnt stop the rest of the application from running. Could this be an alternative to a loop that repeatedly checks GetTickCount and calls doevents?
dilettante 01-02-2008, 05:31 AM I think the main difference is that flushing the message queue that way doesn't give up the processor first. In most programs there is only one Form active (Loaded and not blocked by a modal Show of a child Form) anyway.
At a certain point you have to just fish or cut bait though.
Either you find a way to make DoEvents() work for you, break up the workload along some iteration path and drive it via a Timer or some other technique, or else resort to threading or use a child process. All of these involve some degree of "spaghetti" code. It's the nature of things no matter what programming language you're using.
Cerian Knight 01-02-2008, 07:04 AM Yes most those problems with DoEvents() relates specifically to the 'event spaghetti' I mentioned
One of my colleagues places his only DoEvents call in a callable function called 'CanDoEvents', which is internally flagged to prevent recursion. He swears by this, and I can understand the benefits, but it also means that all critical calls to the function must parse the return value to see if they were allowed the request. By inspection this sounds like a good idea to me, but I haven't tried it myself.
It's the nature of things no matter what programming language you're using.
Sometimes I wish for the old days of Win3.x, where you HAD to give back a slice to keep the OS up. It made programming more disciplined, in a way, I think.
Rockoon 01-02-2008, 06:25 PM You will never find me signing on to coorperative multitasking..
It is precisely a place where the quote "In theory there is no difference between theory and practice, but in practice there is" hits home.
OTOH I am a big proponent of writing friendly to multitasking programs .. always give up timeslices if you do not need them.
You do not need to hog the processor when waiting for an external event. You do not need to render an animation at 500 frames per second. If there is work to be done (any strictly finite task) then by all means be cpu greedy, but be rational about what work really needs to be done.
Any single-threaded loop that takes more than about 1/5th of a second to do its work desperately needs DoEvents() .. your users will be thankfull .. its more work for the programmer but nobody said that writing a responsive application was going to be easy in all cases.
|