Loop to check time in VB.NET - vb.net

So I'm kind of new to VB and am just playing around with a little project, I currently need a loop that is constantly checking the systems clock to see if it's equal to a certain time.
While Not myTime.Hour = 24
If TimeOfDay = newTime Then
nfi.ShowBalloonTip(15)
intRandNumb = RandomNumber(1, 15)
dblAddMinutes = intTime + intRandNumb
newTime = TimeOfDay.AddMinutes(dblAddMinutes)
End If
End While
I have this right now, but obviously it's grinding everything to a halt and using 50% of my cpu in the process, I just would like to know what I can substitute in or change to make this loop run better and perform how I need it to.

you can add
Threading.Thread.Sleep(0),
this will cause a context switch and greatly reduce the CPU usage
Also consider using a timer object to be called every 10 or 100 ms, this will also be better in usage then having a loop

You can use
Threading.Thread.Sleep(0)
This will cause the working thread to yield the rest of it's current timeslice which will reduce the cpu usage quite a bit. However you should consider whether you really nead busy waiting for the time or if you could get away with setting a timer to count down the difference between the current time and the expected time, e.g.:
var t = new System.Timers.Timer((DateTime.Now - DateTime.Now).TotalMilliseconds);
t.Elapsed = DoSomething;
t.Start();

checking the systems clock to see if it's equal to a certain time.
There are two "correct" ways to do this:
Build a normal app that doesn't care what time it is, and set it up in windows as a schedule task.
Check the time once and calculate how long until the desired time. Then set up a timer to wait for that exact duration.
Under no circumstance should you keep polling the system clock for something like this that will just run once.

As Joel pointed out, you should try using a timer instead. I'm not sure if your app is a form or console or other, so I'll try to be generic and use System.Timers.Timer.
The code here (interval is set at 10ms, change to a value of your need):
Private timer1 As System.Timers.Timer
Const interval As Integer = 10
Sub initTimer()
timer1 = New System.Timers.Timer(10)
AddHandler timer1.Elapsed, AddressOf Me.timer_Elapsed
timer1.Start()
End Sub
Sub timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
'do your stuff here
'Console.WriteLine(e.SignalTime.ToString())
End Sub

Related

Visual Basic: Image moves up and and then back down after a button click

I am attempting to make a character appear to jump straight up in the air and then come back down and return to the same level he started at. (y=100) The code below seems to make the program fight itself and move him up and down at the same time.
I have tried countless methods and all of them resulted in the guy either going up and not coming back down or flying off the page.
Private Sub btnJump_Click(sender As Object, e As EventArgs) Handles btnJump.Click
tmrJump.Start()
End Sub
Private Sub tmrJump_Tick(sender As Object, e As EventArgs) Handles tmrJump.Tick
For intCounterUp As Integer = 100 To 15
picSpaceRunner.Location = New Point(intCounterX, intCounterY)
intCounterY = intCounterUp
Next intCounterUp
For intCounterDown As Integer = 15 To 100
picSpaceRunner.Location = New Point(intCounterX, intCounterY)
intCounterY = intCounterDown
Next intCounterDown
End Sub
End Class
The code is running with no delay, so you're at the mercy of the machine.
I'm not a professional game coder, so I couldn't explain the intricacies of modern game engines. However, one of the basic ideas I learned a long time ago is to control your game/animation loop. Consider the frames per second.
In your code, it could be as simple as adding a delay within each loop iteration. If you want the character to complete his jump in 2 seconds (1 second up, 1 second down), then divide 1000 (1 sec = 1000 ms) by the number of iterations in each loop and delay by that amount. For example, you have 85 iterations, so each iteration would take approximately 12 ms.
If you don't mind blocking a thread, you can do this very easily with Threading.Thread.Sleep(12). If blocking is an issue, you'll likely want to use an external timer.
I found this link during a Google search. He explains how to set up a managed game loop in VB.Net.
http://www.vbforums.com/showthread.php?737805-Vb-Net-Managed-Game-Loop
UPDATE: Per OP's comment...
To do this using timers, you'll want to manipulate the character object directly within the Timer event handler (Tick). You wouldn't use loops at all.
Set the Timer's Interval to the value discussed earlier - the number of ms corresponding to how long it takes to move 1 pixel. Then, in the Timer's Tick handler, set the character object's Location equal to a new Point with the new value. Also in the Tick handler, check your upper bound (15), then reverse the process until it hits the lower bound (100).
For example,
Private Sub tmrJump_Tick(sender As Object, e As EventArgs) Handles tmrJump.Tick
If (intCounterY > 15 And blnGoingUp == True) Then
picSpaceRunner.Location = new Point(intCounterX, intCounterY - 1);
End If
... Remaining Code Goes Here ...
End Sub
Do not put the loop in the timer_tick. Increase or decrease the height by set interval instead and then check if the image had reached the maximum or minimum height.

Why is basic time/date output taking two seconds to display?

What I have:
I'm displaying the current time and date (real-time) at the bottom of a form using a timer element.
I'm using two labels to display the time and date respectively.
What I need:
I need the time and date labels to display as instantly as everything else.
My problem:
There is a two second delay in the displaying of the time and date labels.
My code:
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'Format time and date labels.
TimeMain.Text = Format(Now, "hh:mm:ss")
DateMain.Text = Format(Now, "dddd, d/MM/yyyy")
End Sub
Note: The above is preceded by a Form_Load sub that simply defines a default accept button. The above is followed by 5 by five short subs.
Edit:
Though the steps for reproducing the problem have already provided in the comments I've been requested to reiterate here. The only difference between the two code blocks posted in this question is that I've left the label text at default to spare the reproducer having to type anything.
Drag two labels and a timer onto a new form and use the following code:
Public Class Form1
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
'Format time and date labels.
Label1.Text = Format(Now, "hh:mm:ss")
Label2.Text = Format(Now, "dddd, d/MM/yyyy")
End Sub
End Class
For the timer's properties, Enabled is defined as True and Interval as 1000.
I know this isn't exactly an answer but this is too long to fit in a comment. Also read here for a little more information on timers:
Why are .NET timers limited to 15 ms resolution?
Does the System.Windows.Forms.Timer run on a different thread than the UI?
Timer elapsed events from what i understand (which could very well be wrong) aren't guaranteed to fire exactly when the time has elapsed, it's more of... put it in queue to fire once the timer has elapsed.
Imagine your application/timer started at "00:00:01.999" and your label states "00:00:01" as the current time.
Exactly 1000 MS later you're at "00:00:02.999 and the elapsed event fires, completing at "2014-01-01 00:00:03.0045" and your label is updated to "2014-01-01 00:00:03" - you've already "lost" a second here.
You could try setting your interval to something lower than one second (say 750) which would get you a potentially more accurate looking counter. Additionally, ensure you're setting the timer labels on form load. I've not worked very much with timers and i'm having trouble finding the article i was reading earlier but you might need to worry about UI locking depending on the timer type used (there are apparently 4 timer classes in the .net framework.) Perhaps someone else can expand on that though, I don't know much about winforms.

Terminate thread execution for 24 hours

I have my service working mostly as it should, but it should only do it's work once every day.
To arrange this, my boss recommended I sleep the worker thread until 7am tomorrow:
This Thread.Sleep call is copied directly from a similar service he wrote that apparently works, but this always throws an ArgumentOutOfRangeException - the value returned is a negative.
Private Sub startExport()
#If Not Debug Then
Thread.Sleep(1000 * 60 * 1)
#End If
While runReportExport
Try
runExport()
Catch ex As Exception
el.WriteEntry("Error exporting data: {1}")
Finally
'sleep thread until tomorrow 7am
Thread.Sleep(DateTime.Now.Subtract(Date.Today.AddDays(1).AddHours(7)))
End Try
End While
End Sub
I'm fairly confused about how this all works so if anyone can explain the whole timespan thing for me, I'd greatly appreciate it.
On the other hand, my friend recommends I manage the thread execution in a different way.
Here's what He recommended I do:
Private lastExecute As DateTime = DateTime.Now
Private Overrides Sub OnStart(ByVal args() As String)
startService()
End Sub
Private Sub startService()
Dim nextExecute = lastExecute.AddDays(1)
If nextExecute >= DateTime.Now Then
lastExecute = DateTime.Now
tWorker = New Thread(AddressOf startExport)
tWorker.IsBackground = True
tWorker.Start()
End If
End Sub
He said this would execute the worker thread once on startup and not again for another day. While this code does work without error, it doesn't stop the service loop from executing the worker thread over and over again (currently it executes a 2nd time as soon as it finished it's first run)
Personally I'm open to either approach, I can't seem to get either working.
Basically, all I need at the end of the day is a service that exports data once daily.
My boss' Thread.Sleep option seems the simpler, but my friend's suggestion seems like better practice.
Can anyone help me to get this sorted (at this point I don't care which one I use, just so long as it works)?
DateTime.Now.Subtract(Date.Today.AddDays(1).AddHours(7)))
You subtract a future time from the current time. That always produces a negative value. Kaboom.
You'll need to invert that, subtracting the current time from the future time. Spelled out for clarity and avoiding the race on DateTime.Now :
Dim today = DateTime.Now
Dim tomorrow = today.Date.AddDays(1).AddHours(7)
Dim wait = tomorrow - today
Thread.Sleep(wait)
You will need to do something useful when the service is stopped. Best done with a ManualResetEvent, you'll get the sleep from its WaitOne(wait) method. Btw, extremely wasteful to have a thread sleep for that long and not doing anything useful. Use a Timer instead.
Another approach, and one I use, to run a specific task once a day is to set the task up in a timer. In my case I want the task to run at 12:05am each morning. Accordingly, when my program starts I set the initial interval of the timer such that the first tick will happen at 12:05 am the next day. After that, once a day, at the end of the code the executed as part of the tick I once again reset the timer interval such that the next tick will happen at 12:05am the next day.
...
Timer1.Interval = MillisecondsToMidnight() + 300000 ' next tick 12:05:00 tomorrow
...
Private Function MillisecondsToMidnight() As Integer
Dim ReturnValue As Integer
Dim ts As TimeSpan
Dim Tomorrow As DateTime = Today.AddDays(1)
ts = Tomorrow.Subtract(Now)
ReturnValue = ts.TotalMilliseconds()
ts = Nothing
Return ReturnValue
End Function

Console app timer to call web methods every x minutes

I am coding in VB.Net, VS 2008.
I wrote a console app that consumes 2 web methods from a web site application. I need to enhance this console app so that it launches the web methods continuously, perhaps every x minutes (during business hours), but never before the last invocation has terminated, whose duration may vary, depending on how many accounts there are to process.
Originally, I scheduled the application using Task Scheduler, but I think this doesn't prevent two invocations at the same time.
Although I have seen many posts on using timers, I haven't found exactly what I need.
So far I have:
Dim aTimer As New System.Timers.Timer()
AddHandler aTimer.Elapsed, AddressOf TriggerWebMethods
' Set the Interval to 10 minutes:
aTimer.Interval = 1000 * 60 * 10 '(1 second * 60 = 1 minute * 10 = 10 minutes)
aTimer.Enabled = True
aTimer.AutoReset = True
When should Timer.Elapsed be used vs. Timer.Tick?
What is the difference between Timer.Enabled vs Timer.Start, and should I be selecting just one?
I would like the 2nd web method to kick off when the first one is done.
I'd like to keep this as simple as possible. Thank you for all help.
If you are dealing with a System.Timers.Timer, then you'd only have the Elapsed event available. If a System.Windows.Forms.Timer, then you'd use the Tick event. You're not writing a WinForms app so you would be using the System.Timers.Timer.
Personally, I would only use the Enabled property to see if the timer has been started. I wouldn't use it to start or stop it. Using the Start() or Stop() method makes it very clear what's happening to the timer.
If your web methods execute synchronously, you could just call them one after the other in your TriggerWebMethods() method. The second will not be called until the first completes.
Sub TriggerWebMethods(source As Object, e As ElapsedEventArgs)
FirstWebMethod()
SecondWebMethod()
End Sub
If asynchronously, you'd have to register a callback on the first web method to execute the second when it completes. In VB, I believe you can use the second directly as the callback, depending on how you make the asynchronous call. (Sorry, my VB is very rusty now so might not be 100% correct syntax)
Sub FirstWebMethod()
' ...
End Sub
Sub SecondWebMethod()
' ...
End Sub
Sub TriggerWebMethods(source As Object, e As ElapsedEventArgs)
Dim first As Action = AddressOf FirstWebMethod
first.BeginInvoke(AddressOf SecondWebMethod, first)
End Sub
Just to add a little to Jeff M's answer. The Timer.Elapsed Event has the following note.
If the SynchronizingObject property is null, the Elapsed event is
raised on a ThreadPool thread. If the
processing of the Elapsed event lasts
longer than Interval, the event might
be raised again on another ThreadPool
thread. In this situation, the event
handler should be reentrant.
Since you're in a Console app you can either hand roll you own SynchronizingObject or you can set the AutoReset to False, and change your TriggerWebMethods to have a start at the end. You may even want to offset the interval to take into consideration the amount of processing time.
e.g.
Dim start As Date = Now
'do stuff
Dim ts As TimeSpan = Now - start
Dim i As Integer = (1000 * 60 * 10) - ts.TotalMilliseconds
Dim aTimer As Timers
aTimer.Interval = i

How to limit CPU usage in a while loop

How do you limit the CPU of a while loop?
In this case, the code which is inside the while loop:
Private Sub wait(ByVal time)
Dim sw As New Stopwatch
sw.Start()
Do While sw.ElapsedMilliseconds < time And StillOpen = True
Application.DoEvents()
Loop
sw.Stop()
End Sub
But now, here is the issue. This loop is allowing the while loop to run every second, once a second, and the wait sub is causing this delay, as it should.
How can I limit the CPU that this is taking up? For some reason, my task manager says it is taking 50 CPUs to run this simple task, yet it should probably take no more than 1 or 2. Though the manager says it is taking that much CPU, my computer speed is not being affected at all, which is odd considering it is a two-year-old laptop.
I don't want any users to freak out about it, but knowing how people are these days....
Anyway, the language is vb.net. Can someone please help me?
Thanks!
EDIT: To clarify, that code is not inside the while loop itself, but a call for the subroutine is, i.e. wait(1000)
Use a timer event !!! Nearly no cpu effort.
You could always perform some kind of sleep between iterations of the loop...
I'm not familiar with VB.NET but a duration of 100-200ms will probably be more than enough to drop the CPU usage.
Eg:
Do while (...)
Application.blah();
System.Threading.Thread.Sleep(150);
End
Edit After some research, I think the function you want is: System.Threading.Thread.Sleep()
Your code is executing Application.DoEvents() constantly in the while loop, for the time duration specified in your time parameter. This will consume one core of your CPU, which is why you're seeing 50% processor usage (you have a dual-core processor, correct?). This is an ugly way to wait. You could instead call Thread.Sleep(), passing it the number of milliseconds you'd like your thread to wait.
If you'd like your application to stay responsive, you might also spin off a timer, and block the UI from any action until the timer triggers. Something like (lightly tested):
// constructor or designer code
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += new EventHandler(timer_Tick);
void Wait(int interval)
{
timer.Interval = interval;
timer.Start();
BlockUIOperations(); // implement yourself
}
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
EnableUIOperations(); // implement yourself
}
Here's my attempt at a translation into VB:
'' Add a Timer object to the form named "Timer".
'' Hook its Tick event to Timer_Tick
Private Sub Wait(ByVal interval As Integer)
Timer.Interval = interval
Timer.Start()
BlockUIOperations() '' implement yourself
End Sub
Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick
Timer.Stop()
EnableUIOperations() '' implement yourself
End Sub
Well, the CPU is always running at 100% when it's running, so the only practical way to limit the CPU usage is to run bursts or loop and sleeping in between.
Laptop CPUs usually have some SpeedStep technology or equievalent that will slow down the CPU when it's not working hard, but it's not reasonable to assume that your application would have access to control that, at least not directly. You might be able to affect it indirectly by measuring the CPU usage and adjust the length of the work and sleep cycles to get the desired result.
If you don't mind blocking the current thread, you could use a WaitHandle.
Public Sub Wait(ByVal ms As Integer)
Using wh As New ManualResetEvent(False)
wh.WaitOne(ms)
End Using
End Sub
Sub Main()
Console.WriteLine("Hello World!")
Wait(5000)
Console.WriteLine("Good-Bye!")
End Sub
Of course, something more complex can be constructed depending on what you are trying to accomplish.
This is perfect as a VB.net sleep replacement. Now my console app is NOT reported as non responsive since I have no sleep commands!
Just add Imports System.Threading above your module and place this just above your sub main
Public Sub Wait(ByVal ms As Integer)
Using wh As New ManualResetEvent(False)
wh.WaitOne(ms)
End Using
End Sub
Then, in your sub main, use
wait(100)
to pause your app for 100 miliseconds.
Have fun
You should take note of if you are doing this in the main UI Thread or a thread you have spun off.
For Threads the easiest way is to just Thread.Sleep(x miliseconds)
On the main UI thread I tend to use a DoEvents function in vb.net and vb6 like this
Public Sub TimeKiller(byval secondstowait as integer)
dim tmptime as datetime = datetime.now
do while datetime.now < dateadd("s",secondstowait,tmptime)
Application.Doevents
end while
End Sub
On the question of CPU usage I look at it like this.... if you make just a hard loop that like
while true
end while
I would expect to see very high cpu usage over 50% because the UI thread is hard blocking on this.... in most cases the windows system will limit the cpu usage of any given program so that its threads dont block the entire system.
The DoEvents ensure that windows message pumps fire correct and respond to correct. It also ensures that the garbage collector fires on time.
Also if you have other threads spun up off of your UI.Thread your UI.Thread can respond to events fired from these other threads....
In such cases where your calling form controls from other threads and do form.InvokeRequired routines will be able to respond correctly.
Also The only time you should be hard looping on the MainUI thread is when it is in response to some user activity and you need to put waits in for the user to see progress of something....
If it is some kind of automated process that is always running... look to moving it to another thread.
Or if its something that runs periodically on a timer or a time that kicks off a thread.
Somebody please tell me if I am wrong on these assumptions....
Not sure about the Using wh As New ManualResetEvent(False) wh.WaitOne(ms) as I have never heard of that and have no idea what that does.