Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Timer or Sleep API for Delay. Which is better?

Status
Not open for further replies.

TheVampire

Programmer
May 1, 2002
828
0
0
US
Strongm and CCLINT have made the assertation that using the sleep API call for a delay is not as desirable as using a timer.

Since we haven't had a discussion like this in a while, I thought that it would make good fodder for such.

Personally, I prefer using the Sleep API for a delay, for the following reasons.

1: It's one less control on the form.

2: It's easier to use and requires less code.

3: I think it's more precise.

I'd like to hear reasons pro / con for each.

Robert
 
1. You don't need a control for timers (it's just a bit more work to do it without)
2. Maybe. It also stops your VB application dead in its tracks. You can kind of work around this by breaking the delay you want into smaller bits and looping - but then the 'advantage' of less code is lost.
3. It uses the same system timer resolution as the Timer control
 
1: Yes, you can use a timer without a form by creating one through the API. But like you said, it's more work.

2: I would say that for "short" delays, ( Several seconds at most ) then I would use Sleep. Anything longer than that and a timer might be more appropriate if you need the capability to "break out" of the delay.


Robert
 
A big part of the question is why are you delaying, and what you want to allow during the delay period.

If for example, you want your process to remain alive, and to process events, while not allowing any user interaction while you're in delay mode, then you cannot use the Sleep API, as you process will be suspended, and none of the events will be processed.

On the other hand, if you want your process to cease processing all events during the delay period, then the Sleep API may be a good choice.

You need to understand how a Timer (either system timer, or control timer) and the Sleep API affect your process, and balance that against your functional requirements to know which is the better choice in any given situation. Good Luck
--------------
As a circle of light increases so does the circumference of darkness around it. - Albert Einstein
 
It's even better to use SetWaitableTimer, which will allow the screen to repaint, receive DDE messages, and so forth.

Code:
Public Type FILETIME
 dwLowDateTime As Long
 dwHighDateTime As Long
End Type

Public Const WAIT_ABANDONED = &H80&
Public Const WAIT_ABANDONED_0 = &H80&
Public Const WAIT_FAILED = -1&
Public Const WAIT_IO_COMPLETION = &HC0&
Public Const WAIT_OBJECT_0 = 0
Public Const WAIT_OBJECT_1 = 1
Public Const WAIT_TIMEOUT = &H102&

Public Const INFINITE = &HFFFF
Public Const ERROR_ALREADY_EXISTS = 183&

Public Const QS_HOTKEY = &H80
Public Const QS_KEY = &H1
Public Const QS_MOUSEBUTTON = &H4
Public Const QS_MOUSEMOVE = &H2
Public Const QS_PAINT = &H20
Public Const QS_POSTMESSAGE = &H8
Public Const QS_SENDMESSAGE = &H40
Public Const QS_TIMER = &H10

Public Const QS_MOUSE = (QS_MOUSEMOVE _
Or QS_MOUSEBUTTON)

Public Const QS_INPUT = (QS_MOUSE _
Or QS_KEY)

Public Const QS_ALLEVENTS = (QS_INPUT _
Or QS_POSTMESSAGE _
Or QS_TIMER _
Or QS_PAINT _
Or QS_HOTKEY)

Public Const QS_ALLINPUT = (QS_SENDMESSAGE _
Or QS_PAINT _
Or QS_TIMER _
Or QS_POSTMESSAGE _
Or QS_MOUSEBUTTON _
Or QS_MOUSEMOVE _
Or QS_HOTKEY _
Or QS_KEY)

Public Declare Function CreateWaitableTimer Lib "kernel32" _
Alias "CreateWaitableTimerA" ( _
Byval lpSemaphoreAttributes As Long, _
Byval bManualReset As Long, _
Byval lpName As String) As Long


Public Declare Function OpenWaitableTimer Lib "kernel32" _
Alias "OpenWaitableTimerA" ( _
Byval dwDesiredAccess As Long, _
Byval bInheritHandle As Long, _
Byval lpName As String) As Long


Public Declare Function SetWaitableTimer Lib "kernel32" ( _
Byval hTimer As Long, _
lpDueTime As FILETIME, _
Byval lPeriod As Long, _
Byval pfnCompletionRoutine As Long, _
Byval lpArgToCompletionRoutine As Long, _
Byval fResume As Long) As Long


Public Declare Function CancelWaitableTimer Lib "kernel32" ( _
Byval hTimer As Long) As Long


Public Declare Function CloseHandle Lib "kernel32" ( _
Byval hObject As Long) As Long


Public Declare Function WaitForSingleObject Lib "kernel32" ( _
Byval hHandle As Long, _
Byval dwMilliseconds As Long) As Long


Public Declare Function MsgWaitForMultipleObjects Lib "user32" ( _
Byval nCount As Long, _
pHandles As Long, _
Byval fWaitAll As Long, _
Byval dwMilliseconds As Long, _
Byval dwWakeMask As Long) As Long

Public Sub sWait(Byval lngNumSecs As Long)

 '--- Pauses the app's process threads for a specified number of seconds
 '--- Allows the screen to redraw while threads are paused


 '--- Parameter
 ' [In]
 ' lngNumSecs: number of seconds to pause


 Dim dblDelay As Double
 Dim dblDelayLow As Double
 Dim dblUnits As Double
 Dim lngBusy As Long
 Dim lngRtn As Long
 Dim hTimer As Long
 Dim strTimerName As String
 Dim udtFT As FILETIME


 strTimerName = "MyApp Timer" & Chr$(0)
 hTimer = CreateWaitableTimer(0, True, strTimerName)

 udtFT.dwLowDateTime = -1
 udtFT.dwHighDateTime = -1
 lngRtn = SetWaitableTimer(hTimer, udtFT, 0, 0, 0, 0)

 'Convert the units to nanoseconds
 dblUnits = Cdbl(&H10000) * Cdbl(&H10000)
 dblDelay = Cdbl(lngNumSecs) * 1000 * 10000

'By setting the high/low time to a negative number, it tells
'the Wait (in SetWaitableTimer) to use an offset time as
'opposed to a hardcoded time. If it were positive, it would
'try to convert the value to GMT
 udtFT.dwHighDateTime = -Clng(dblDelay / dblUnits) - 1
 dblDelayLow = -dblUnits * (dblDelay / dblUnits - _
 Fix(dblDelay / dblUnits))

 'Check we don't exceed storage capacity, H80000000 is max Long
 If dblDelayLow < Cdbl(&H80000000) Then
  dblDelayLow = dblUnits + dblDelayLow
 End If
 udtFT.dwLowDateTime = Clng(dblDelayLow)
 lngRtn = SetWaitableTimer(hTimer, udtFT, 0, 0, 0, False)

 Do
  'QS_ALLINPUT means that MsgWaitForMultipleObjects will
  'return every time the thread in which it is running gets
  'a message
  lngBusy = MsgWaitForMultipleObjects(1, hTimer, False, _
  INFINITE, QS_ALLINPUT)
  DoEvents
 Loop Until lngBusy = WAIT_OBJECT_0

 'Close the handle
 CloseHandle hTimer

End Sub

Paul Bent
Northwind IT Systems
 
Paul,

Would this code work if you compiled it into a DLL ( adding the appropriate properties and methods ) and referenced the DLL in your program?

Robert
 
I may be miss-understanding things here but, I need to process a section of code every five minutes, the code is to run on a Win2K box and I want it to run as a service with no user interface.

The code and service runs fine, but my delay code....

Private Sub wait(ApproxSeconds As Long)
Dim waittil
'/// Pause without using a form timer
waittil = DateAdd(&quot;s&quot;, ApproxSeconds, Now)

Do While Now < waittil
DoEvents
Loop
End Sub

...causes the app to use between 70% and 80% of processor usage in Task Manager. Therefore I thought the code above would help, I was wrong as the code above still uses a loop with DoEvents to perform the wait even though it allows the app to look alive.

So my comments / questions are
1. Win2K seems to think my app is using 75% of the processor all the time the wait code is running, is this the truth or a figment of its imagination?<g>
2. If it is using the processor that much, what do I use instead of my code or the sample in the previous post to trigger my function on a regular basis?

I think I want an API timer event that in VB class terms triggers an event that I can capture. (hey, I know what I mean even if you dont!!)

Thanks in advance for any comments

Al
 
That loop is not a good idea for longer waits (maybe over 2 or 3 seconds).
Yep, it does put a great load on the processor.
To process a section of code every 5 minutes just use a vb timer or the api timer.

 
alweb99,

What we were discussing in this thread was if you need a &quot;delay&quot; in a program, which method was more appropriate to use.

In your case, I think that a timer would be a good choice.

If you do not have a form to place a timer control on, you can create a timer with the API. Just use it to count up until you reach your set time, and then perform whatever code you want after the time runs out.

Robert
 
TheVampire,

Yes, I'm pretty sure it would as the DLL is running in the same process as your program.

I've also used it to control an app's CPU usage during a For...Next loop by pausing for 1 sec every n'th iteration. It was particularly useful in one job where I was automating Outlook with late binding, parsing loads of stuff from messages and querying/updating a database. After a few mins CPU usage hit 100% unless I paused the thread every few secs.

Paul Bent
Northwind IT Systems
 
alweb99,
I suggest you use a Timer, with a Static variable to hold the counter (Timer only runs to 65535 millisecs, just over a minute)
Set Timer1.Interval to 60000, Enabled = True

Private Sub Timer1_Timer()
Static temp
If temp = 4 Then
MsgBox &quot;It's time&quot;
temp = 0
Else
temp = temp + 1
End If

End Sub

will give you the message every 5 minutes
________________________________________________________________
If you want to get the best response to a question, please check out FAQ222-2244 first

'People who live in windowed environments shouldn't cast pointers.'
 
For a 5 minute timer, I would suggest that you use the SetTimer API. As has been pointed out already, the intrinsic Timer control has a 65 second limitation because the maximum timer interval is a unsigned integer value of milliseconds (65535).

The System Timer, using the SetTimer API does not suffer this limitation as it uses a long value (up to just under 47 days) for its interval which means that you quite easily set the timer for 5 minutes (300000) without having to go thru the trouble of counting the minutes between timer events. You can also use this timer in a module inside of a program without any forms at all.

The API declaration is as follows:
Code:
Public Declare Function SetTimer Lib &quot;user32&quot; (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib &quot;user32&quot; (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Dim glTimerID as Long ' Timer ID Number
and inside the module (perhaps in Sub Main) you add 1 line of code to activate the timer:
Code:
glTimerID  = SetTimer(0, 1, 300000, AddressOf MyTimerHandler)
and at the appropriate time, kill the timer
Code:
KillTimer 0, glTimerID
and of course, in the module, write you event handler, same name as in the SetTimer call
Code:
Private Sub MyTimerHandler
   <code to execute>
End Sub
Again, two major advantages - does not require a form, and interval not restricted to 65 seconds. Good Luck
--------------
As a circle of light increases so does the circumference of darkness around it. - Albert Einstein
 
But for a 5 minute interval the vb timer control works quit nice.

 
But remember when using SetTimer API, that if you fail to use KillTimer before the program exits ( running from the designer ) that it will crash the VB designer.

Say for example you run into a bug while the timer is running, and you used END to stop the program. The designer will crash.

Strongly suggest that you save your work before running any program that uses this API.

Robert
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top