Beginner: Window Not Responding While Continuous Loop Executes

  • Thread starter Thread starter Shamrokk
  • Start date Start date
S

Shamrokk

My application has a loop that needs to run every 2 seconds or so. To
acomplish this I used...
"Thread.Sleep(2000);"

When I run the program it runs fine. Once I press the button that starts the
looping function the window becomes unmovable and cannot close under its own
direction (the upper right "close 'X'")

My first attempt to solve the problem was to have the looping function
execute as its own thread, the idea being this would leave the main thread
open to keep the window intact while the looping function executed in the
background of the program.

Here's a basic breakdown of the code...



public void btnStart_Click(object sender, System.EventArgs e)

{

Thread GoThread = new Thread(new ThreadStart(Go));

GoThread.Start();

}


public void Go()

{

//loop code here withe Thread.Sleep(2000); inside the end of each loop

}



My question is...

How do I keep the loop running without the window locking up?
 
As you've discovered, Thread.Sleep is a bit too effective -- it stops
everything in its tracks for the period specified. Running a separate
thread is overkill. The better approach would just be to add a
System.Windows.Forms.Timer object to your form. (It's located under the
Components tab of the toolbar.)

The name is a bit deceptive -- it's not for timing your code. Instead, it's
like an egg timer -- it sits quietly for a predetermined period, and then
raises a Tick event ("bing!") when the time's up. If you set it correctly,
it will repeat the cycle indefinitely.

You insert your "run this every two seconds" code in the event handler for
Timer.Tick. That way, the code will run whenever the Timer tells it to, and
the program will remain responsive in the in-between periods. There are
detailed examples in the help files.

--Robert Jacobson
 
My first question is for the OP: what was wrong with the code you posted.
That shouldn't lock up the UI - unless you are updating the UI from your
worker thread. Make sure you don't do that. see:
http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.aspx

Depending upon how much "work" you are doing in that two-second loop, the
Windows.Forms.Timer may or may not be the ideal solution. If you want to
update a control (like change the text in a label), then it is ideal -
because the "Tick" event will be on the main UI thread - so there are less
"multi threading" issues. In fact, you still just have a single thread.
You can treat it almost exactly like a button being pressed every two
seconds.

However, if you want to go do real work every two seconds (even, say, check
the status of some file, where a network delay could potentially cause a
substantial slowdown) then this is not the solution. If you try to do this
from the UI thread, then your app will come to a crawl (as you noticed
before). I'd either stick with starting a new thread like the code posted,
or looking at using something from the threadpool - possibly like this
(where Go and Finish are two methods you create).
MethodInvoker mi = new MethodInvoker(Go);

mi.BeginInvoke(new AsyncCallback(Finished), null);
 
Thank you both Robert and Michael for your replies.

I used the timer and the code is running fairly smooth now. However,
Michael pointed out a couple of things that brought up further questions as
I still prefer the looping method.

The code for the most part is running fairly smooth with the exception of a
hiccup of 100ms-200ms every two seconds. I'd very much like to rid the
program of that hiccup. I'm also concerned about a situation Michael
mentioned where the code in question may take more than two seconds to
execute, bringing the program to a grinding slowdown. In rare cases some
funtions called in the code are still using the Thread.Sleep method, it
hasn't happened yet but the situation will occur eventually.

The code I used to create my own thread did not solve the problem and is
actually mentioned in the MSDN arcticle Michael posted as being in violation
of the single most important rule of threading...

"As soon as you introduce multiple threads, however, you need to understand
the single most important rule of threading in Windows Forms: with very few
exceptions, you must never use any member of a Control on any thread other
than the one that created it."--<a
href="http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx">http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx</a>

Using the Threadpool seems like the way to go. It is an option I was
completly unaware of until now. Thanks very much for your time and the link
to the article, they have been a great help. I havn't had time to try and
impliment the new code yet but if I need more help I'll be sure to ask.

Thanks again,

Patrick
 
Thank you both Robert and Michael for your replies.

I used the timer and the code is running fairly smooth now. However,
Michael pointed out a couple of things that brought up further questions as
I still prefer the looping method.

The code for the most part is running fairly smooth with the exception of a
hiccup of 100ms-200ms every two seconds. I'd very much like to rid the
program of that hiccup. I'm also concerned about a situation Michael
mentioned where the code in question may take more than two seconds to
execute, bringing the program to a grinding slowdown. In rare cases some
funtions called in the code are still using the Thread.Sleep method, it
hasn't happened yet but the situation will occur eventually.

The code I used to create my own thread did not solve the problem and is
actually mentioned in the MSDN arcticle Michael posted as being in violation
of the single most important rule of threading...

"As soon as you introduce multiple threads, however, you need to understand
the single most important rule of threading in Windows Forms: with very few
exceptions, you must never use any member of a Control on any thread other
than the one that created it."--<a
href="http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx">http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx</a>

Using the Threadpool seems like the way to go. It is an option I was
completly unaware of until now. Thanks very much for your time and the link
to the article, they have been a great help. I havn't had time to try and
impliment the new code yet but if I need more help I'll be sure to ask.

Thanks again,

Patrick
 
Thank you both Robert and Michael for your replies.

I used the timer and the code is running fairly smooth now. However,
Michael pointed out a couple of things that brought up further questions as
I still prefer the looping method.

The code for the most part is running fairly smooth with the exception of a
hiccup of 100ms-200ms every two seconds. I'd very much like to rid the
program of that hiccup. I'm also concerned about a situation Michael
mentioned where the code in question may take more than two seconds to
execute, bringing the program to a grinding slowdown. In rare cases some
funtions called in the code are still using the Thread.Sleep method, it
hasn't happened yet but the situation will occur eventually.

The code I used to create my own thread did not solve the problem and is
actually mentioned in the MSDN arcticle Michael posted as being in violation
of the single most important rule of threading...

"As soon as you introduce multiple threads, however, you need to understand
the single most important rule of threading in Windows Forms: with very few
exceptions, you must never use any member of a Control on any thread other
than the one that created it."--<a
href="http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx">http://msdn.microsoft.com/msdnmag/issues/03/02/Multithreading/default.
aspx</a>

Using the Threadpool seems like the way to go. It is an option I was
completly unaware of until now. Thanks very much for your time and the link
to the article, they have been a great help. I havn't had time to try and
impliment the new code yet but if I need more help I'll be sure to ask.

Thanks again,

Patrick
 
Michael raised some good points about performance issues. How about
inserting an Application.DoEvents() call in your loop? I might be wrong,
but I think it will should the form to remain responsive during the loop, at
the expense of slowing performance of the loop itself. (It would be best
not to call it during in every loop.)
 
Shamrokk said:
Thank you both Robert and Michael for your replies.
You're welcome
I used the timer and the code is running fairly smooth now. However,
Michael pointed out a couple of things that brought up further questions
as I still prefer the looping method.

I just had another thought on the timer, well, it's just an example of what
I mentioned the other day and another reason why I don't care much for
timers (at least for a lot of cases)...

I have a C# picviewer screensaver. The user sets how often a new pic is
displayed (down to 1 second). My first implementation was a simple timer,
everytime it "ticked", I'd change the pic shown. Unfortunately, if a really
big pic came up (say a 20 megapixel panorama) it could easily take 10
seconds to load, resize, and display it. In the meantime, 10 timer ticks
could expire and cause absolute mayhem. In this case, I solved it by doing
this in the timer tick event:

timer.Enabled = false;
showNextPic();
timer.Enabled = true;

For some reason, I don't much like that code. It works, so I'm not changing
it.. but it just doesn't feel right to me. It may help you and work for
you, so that's why I decided to mention it.
The code for the most part is running fairly smooth with the exception of a
hiccup of 100ms-200ms every two seconds. I'd very much like to rid the
program of that hiccup. I'm also concerned about a situation Michael
mentioned where the code in question may take more than two seconds to
execute, bringing the program to a grinding slowdown. In rare cases some
funtions called in the code are still using the Thread.Sleep method, it
hasn't happened yet but the situation will occur eventually

Going to a separate thread should cure all of that, unless you have a big UI
update to perform (like drawing a big picture to the background of a form -
that's almost always going to cause a "hiccup" though). But you'll have new
issues of making sure you lock out concurrent access to any objects. In an
ideal case, all object references can be held private to the class that runs
on the worker thread, with a few simple UI updates which are easily done
with Control.Invoke. In a more difficult case, things may need "a little
care" (to quote somebody wise, don't remember who).
Using the Threadpool seems like the way to go. It is an option I was
completly unaware of until now. Thanks very much for your time and the link
to the article, they have been a great help. I havn't had time to try and
impliment the new code yet but if I need more help I'll be sure to ask.

The AsyncOperation class in that sample should be pretty self-expanatory.
And another good thing, it does use the ThreadPool. All you do is override
DoWork with something like:

protected override void DoWork()
{
doStuff(); // call some other method in the class to do your stuff
Thread.Sleep (2000); // kindof a hack
if (CancelRequested) {
AcknowledgeCancel();
return;
}
}

The line Thread.Sleep(2000); should really be (I believe) a combination of
Monitor.Wait and (somewhere else) Monitor.Pulse for a specifc object used
for canceling. I'll try to throw together an example showing this.

The UI then just calls Start(). Add events into your class and use the
AsyncOperation's FireAsync method to raise those events. Pretty easy.
 
Back
Top