Resolved Question About While/Do While Loop

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
162
Programming Experience
Beginner
I want my program to start as close to 13:00:00.000 as possible. To achieve this, I setup a basic while loop..

VB.NET:
        Dim time As DateTime = DateTime.Now

        While time.ToString("HH:mm:ss.fff") <= txtStartTime.Text.Trim
            time = DateTime.Now
            txtLog.AppendText("WAITING at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)
        End While

        Thread1.RunWorkerAsync()
        txtLog.AppendText("THREADS STARTED at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)

When running the code above, I found it takes roughly .005 or so to iterate through the loop. With that in mind, the idea of getting it to start at exactly 13:00:00.000 every single time is probably not possible, which is fine because I just want to be very close. Here is a little excerpt of my log from a random test..

VB.NET:
WAITING at: 17:40:29.974
WAITING at: 17:40:29.980
WAITING at: 17:40:29.984
WAITING at: 17:40:29.989
WAITING at: 17:40:29.994
WAITING at: 17:40:29.999
WAITING at: 17:40:30.003
THREADS STARTED at: 17:40:30.009

My question pertains specifically to the last two lines. The loop does its last check at 17:40:30.003, but then has to iterate and go back up to the beginning of the loop again before it finally exits and starts the thread. Because of that, you can see it actually wastes .006 of time between the last "WAITING at" and the "THREADS STARTED at"

I decided to try two different solutions to fix this. The first was changing my While loop to a Do While loop where I could do my time comparison at the end of the loop instead of the start. I've also tried doing a basic Do loop and then putting an IF statement in there at the end. Here is the code I've tried:

VB.NET:
        Dim time As DateTime = DateTime.Now

        Do
            time = DateTime.Now
            txtLog.AppendText("WAITING at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)
        Loop While time.ToString("HH:mm:ss.fff") <= txtStartTime.Text.Trim

        Thread1.RunWorkerAsync()
        txtLog.AppendText("THREADS STARTED at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)

and

VB.NET:
        Dim time As DateTime = DateTime.Now

        Do
            time = DateTime.Now
            txtLog.AppendText("WAITING at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)
            If time.ToString("HH:mm:ss.fff") >= txtStartTime.Text.Trim Then Exit Do
        Loop

        Thread1.RunWorkerAsync()
        txtLog.AppendText("THREADS STARTED at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)

However, it doesn't seem to fix this issue. It still takes additional time after the last check to iterate through the loop and start the thread. Here's a log from each of the loops above..

Do While Loop
VB.NET:
WAITING at: 17:54:29.967
WAITING at: 17:54:29.973
WAITING at: 17:54:29.978
WAITING at: 17:54:29.984
WAITING at: 17:54:29.991
WAITING at: 17:54:29.998
WAITING at: 17:54:30.004
THREADS STARTED at: 17:54:30.012

Do Loop
VB.NET:
WAITING at: 17:57:29.975
WAITING at: 17:57:29.980
WAITING at: 17:57:29.985
WAITING at: 17:57:29.991
WAITING at: 17:57:29.996
WAITING at: 17:57:30.000
THREADS STARTED at: 17:57:30.005

Considering the Do While loop is actually comparing the time at the end of the loop and not the beginning, shouldn't it start right away without iterating through again? For instance, if my last check is at .004 shouldn't my thread be starting right away at .004 or .005 and not .012 like in my log above? All I want to do is eliminate the small amount of time between the last "WAITING at" and the "THREADS STARTED at"...
 
Solution
It looks like you're starting the Timer in the DoWork event handler of a BackgroundWorker. That method is executed in a background thread. The WinForms Timer is specifically intended for a single-threaded environment, so you need to start it on the UI thread.

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,849
Location
Sydney, Australia
Programming Experience
10+
I haven't read most of your thread because what you're doing is a very bad idea. What you should do is get the current time when you start, subtract that from the time you want to run your code, convert that TimeSpan to milliseconds, assign that value to the Interval of a Timer and then Start that Timer. The Timer will then raise an event very close to the desired time. If you use a Windows.Forms.Timer then it will raise its Tick event on the UI thread. If you use a Timers.Timer then it will raise its Elapsed event on a background thread by default.
 

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
162
Programming Experience
Beginner
I haven't read most of your thread because what you're doing is a very bad idea. What you should do is get the current time when you start, subtract that from the time you want to run your code, convert that TimeSpan to milliseconds, assign that value to the Interval of a Timer and then Start that Timer. The Timer will then raise an event very close to the desired time. If you use a Windows.Forms.Timer then it will raise its Tick event on the UI thread. If you use a Timers.Timer then it will raise its Elapsed event on a background thread by default.

Thanks for your reply jmcilhinney. I hadn't thought of trying it that way, so you've given me another avenue to explore. So, basically have the timer tick one time and start the thread inside the timers tick sub?
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,849
Location
Sydney, Australia
Programming Experience
10+
Thanks for your reply jmcilhinney. I hadn't thought of trying it that way, so you've given me another avenue to explore. So, basically have the timer tick one time and start the thread inside the timers tick sub?
Correct. It's much the same thing as handling the Click event of a Button rather than continually checking to see whether the Button has been clicked.
 

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
162
Programming Experience
Beginner
Correct. It's much the same thing as handling the Click event of a Button rather than continually checking to see whether the Button has been clicked.

I've tried doing what you suggested by adding a timer to my form using the toolbox, however my thread never seems to start. It's almost like my timer doesn't tick when it needs to.. here is my code:

VB.NET:
    Private Sub StartThread_DoWork(sender As Object, e As DoWorkEventArgs) Handles StartThread.DoWork
        Dim startTime As DateTime = txtStartTime.Text.Trim    'This is normally 13:00:00.000
        Dim endTime As DateTime = DateTime.Now.ToString("HH:mm:ss.fff")
        Dim timeSpan As TimeSpan = startTime - endTime
        StartTimer.Interval = timeSpan.TotalMilliseconds
        StartTimer.Start()
    End Sub

    Private Sub StartTimer_Tick(sender As Object, e As EventArgs) Handles StartTimer.Tick
        Thread1.RunWorkerAsync()
        txtLog.AppendText("THREAD STARTED at: " & Format(Now, "HH:mm:ss.fff") & vbCrLf)
        StartTimer.Stop()
    End Sub

any suggestions? Thanks!
 

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,675
Location
Norway
Programming Experience
10+
Debug the values. I think you have swapped them. Future has a higher numeric value than now, so logical calculation is (future-now)=positive value to set as interval.
 

jmcilhinney

VB.NET Forum Moderator
Staff member
Joined
Aug 17, 2004
Messages
14,849
Location
Sydney, Australia
Programming Experience
10+
It looks like you're starting the Timer in the DoWork event handler of a BackgroundWorker. That method is executed in a background thread. The WinForms Timer is specifically intended for a single-threaded environment, so you need to start it on the UI thread.
 
Solution

JohnH

VB.NET Forum Moderator
Staff member
Joined
Dec 17, 2005
Messages
15,675
Location
Norway
Programming Experience
10+
Debug the values. I think you have swapped them. Future has a higher numeric value than now, so logical calculation is (future-now)=positive value to set as interval.
Nevermind, I just misread it based your variable naming (start-end), seeing your start is future and your end is now.
 

digitaldrew

Well-known member
Joined
Nov 10, 2012
Messages
162
Programming Experience
Beginner
It looks like you're starting the Timer in the DoWork event handler of a BackgroundWorker. That method is executed in a background thread. The WinForms Timer is specifically intended for a single-threaded environment, so you need to start it on the UI thread.

Thanks, moving it from the thread into my button did the trick!
 
Top