Threading in a windows form
In this chapter I will examine threading support in the .NET. To visualize
and try the possibilities I will build a multithreaded Windows form. In the process of
building the app we will meet some nice aspects of the C# language and the .Net
FCL in general.
Introducing the thread class
The processor of your PC can only do one thing at a time and we are used to
programs executing statement after statement. But actually your PC is doing
several things at the same time. While you are editing your source code, your
email application is collecting new messages. The processor is still
executing one instruction at a time but in the background all the
different processes each get their share of processing power. This happens in
such tiny time-slices that your programs seem to run continuously.
Each program is, in Windows terms, called a process. Windows takes care of
all processes running getting their time slices. But inside a process it is
possible to do several things at the same time as well. While you are typing a mail
message the same email program is receiving new messages. These different pieces
of code running inside a process (program) are called threads.
In the .net FCL there is the System.Threading namespace dedicated to threading, central is
the Thread class. I will use an object of this class which will show its activity
by constantly moving a trackbar on a windows form. While this trackbar is moving
you can use other controls on the form to call methods on that thread object.

This is a multithreaded application, the UI thread handles the user interaction
with the form and the worker thread moves the bar. In the process of
building this application I have to take some slippery side-steps. So please do
not start running any code until you have read to the end.
The main UI thread
Every application has a main thread. In the code of the main form the
application's threading model is set as a single apartment by applying the
STAThread (Single Threaded Apartment) attribute
to the Main method.
///
<summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void
Main()
Apartments have a lot to to with (Windows) message loops and the UI in
general. When you're not going to dive deep into COM you don't need to know
anything about apartments. I will get back to this threading model later when I
will start to care about thread safety.
The main thread is named the UIthread. It can be accessed through the static CurrentThread
method of the Thread class after which its properties can be set. In the
constructor of the form I will code this
Thread.CurrentThread.Name = "Main thread";
Thread.CurrentThread.Priority = ThreadPriority.Highest;
By default a thread does not have a name, setting it will make it easy to
identify a thread. When more than one thread is running the threading system can
give one thread priority by setting its priority property. I will give the main
thread top priority as it handles the user interaction, giving that top priority
will make the application responsive.
Handling exceptions
There is a lot that can go wrong when working with threads and many methods
of the Thread class throw exceptions to
indicate the need of intervention. I will give my app a helper method to report
these exceptions on the form
private void
reportException(Exception e)
{
textBox1.Text = Thread.CurrentThread.Name +
System.Environment.NewLine +
e.GetType().Name + System.Environment.NewLine +
e.Message;
}
The method will report the name of the thread in which the exception was
thrown, the name of the exception thrown and the exception message itself. The
strings are separated by the System.Newline string, which will be a CR/LF
in Windows.
The worker thread
For the second, worker, thread I will create a thread object. I need some code which will run when the thread starts executing. This
code is a method of the form :
private void
KeepAlive()
{
while
(true)
{
try
{ trackBar1.Value+= 1; }
catch
(Exception ex) { reportException(ex); }
}
}
All the method does is try to move the trackbar one step further. Eventually
the trackbar will reach its maximum value, to reset it I have set an
eventhandler on the value-change event of the trackbar. As soon as the trackbar
reaches its maximum value the eventhandler will reset the value to its minimum:
private void
trackBar1_ValueChanged(object sender,
System.EventArgs e)
{
if
(trackBar1.Value == trackBar1.Maximum)
trackBar1.Value = trackBar1.Minimum;
}
To get the KeepAlive method executed in the worker thread a delegate
is used. The ThreadStart delegate, found in the System.Threading
namespace, has a very simple signature; it has no parameters and a void return
type. The KeepAlive method has this signature.
Now I can create the thread object
t = new Thread(new
ThreadStart(KeepAlive));
t.Name = "Second thread";
t.Priority = ThreadPriority.BelowNormal;
After creating the thread I will give it a name and set its priority to BelowNormal,
in the competition for processing resources the main thread now has an extra
advantage. The thread is ready but it's code will not execute yet.
Timers
Timers have been around for quite some time in Windows. They behave like
a thread, once started they run independently somewhere in the background to fire an event
once in a predefined interval. The code in this event will interrupt any other
code running. I will add a timer to the form, once started the timer will report the status of the thread
1000 times a second. To do so I set the timer's interval property to 1 (millisecond),
and write the following code
private void
timer1_Tick(object sender, System.EventArgs e)
{
if
(ts != t.ThreadState)
{
listBox1.Items.Add(t.ThreadState.ToString());
ts = t.ThreadState;
}
}
The thread's state is kept in the private ts field, when it has really
changed the new state will be reported to the listbox. The type of the ThreadState
property is an bitflag enumeration of type System.Threading.ThreadState, the
state of a thread is described by a combination of members. Its ToString method
translates the state to a readable description of this state. After the thread is
created it will read UnStarted.
Starting and pausing the thread
The code in the thread will start executing by calling the Start
method of the thread object
try { t.Start(); }
catch(Exception ex) { reportException(ex); }
The result will be that the listbox will display Running and trackbar
starts moving in a rapid pace. To slow down the thread and to give other threads
and processes more processor resources a thread can be put to sleep for a give
amount of time. I will add a checkbox and a second trackbar which the thread's
code will check to see if a pause is desired and how long that pause will be:
private void
KeepAlive()
{
while
(true)
{
try
{
trackBar1.Value+= 1;
if (checkBox1.Checked)
Thread.Sleep(trackBar2.Value * 100);
}
catch (Exception ex) { reportException(ex); }
}
}
The Sleep method is a static
method of the Thread class, it puts the current active thread to sleep. The
parameter indicates the amount of time in milliseconds that the thread is
supposed to sleep. It is not possible to put a thread to sleep from another
thread, I cannot code t.Sleep(100), the compiler will detect this. So I
have to code the Sleep statement inside the threads code.
When the checkkox is checked the trackbar will move in steps, the pace of the
steps can be set with the second trackbar. The thread's state will be reported
as WaitSleepJoin.
There is not much to do in the thread, the chance that the timer will
examine the thread while actually running is very small.
So the runtime threading system gives each thread its share of processing
resources. It does so by stopping one thread and starting the next one. Without
taking precautions the thread could be stopped at any point in the code. This
could be quite undesirable, as it could leave the data the thread is
manipulating in an unstable state. It is possible to shield of pieces of code in
so called critical sections, once a critical section is started the thread cannot be stopped
or interrupted until the section has finished. The easiest way to do this is
using the lock statement :
private void
KeepAlive()
{
while
(true)
{
try
{
lock(this)
{
trackBar1.Value+= 1;
}
if (checkBox1.Checked)
Thread.Sleep(trackBar2.Value * 100);
}
catch
(Exception ex) { reportException(ex); }
}
}
The lock statement tries to insure that the update of the trackbar is
always completed. The trackbar could be changed by the user with the mouse as
well, one of the main reasons for locking is to prevent the corruption of data
which can be accessed by several threads.
The application has two threads, the UIthread and the worker thread. The code
of the worker thread consist of the KeepAlive method. The KeepAlive
method accesses controls on the form. Trying to prevent conflicts the worker
thread locks the control it is accessing. This approach does work for "normal"
.net objects but it does not work for accessing objects with a Windows handle.
Controls that can receive UI messages like mouse- or keyboard-clicks do have a
Windows handle. These objects can only be directly accessed from the thread on
which they were created. The controls were created in the form's constructor,
that is the main UI-thread. So the code is not thread safe yet.
The methods and properties of a controls can be safely accessed using the
controls Invoke method. The invoke method takes a delegate as parameter.
A delegate is an object which wraps up a method
of an object, they are the counterpart of method pointers in classical Windows.
For a full explanation of delegates and events you are welcome to
a paper on
the dotnetjunkies site.
I will move all code which accesses controls to a new method :
private int FrmCallback()
{
trackBar1.Value+= 1;
if (checkBox1.Checked)
return
trackBar2.Value * 100;
else
return
0;
}
The method advances the trackbar and returns the amount of time to sleep. I
also need a delegate which describes the signature of this method
delegate
int MyDelegate();
With these two I can create a delegate object. The constructor of the
delegate takes the method to execute as a parameter.
new MyDelegate(FrmCallback)
The implementation of the KeepAlive method changes to :
private
void KeepAlive()
{
while (true)
{
try
{
int Sleep = (int) this.Invoke(
new MyDelegate(FrmCallback) );
if (Sleep != 0)
Thread.Sleep(Sleep);
}
catch
(Exception ex) { reportException(ex); }
}
}
The constructed delegate object is passed to the Invoke method of
this, being the form itself. Which will result in the FrmCallBack
method executing. The result of that method invocation is typecasted to an
integer to get the sleep period. Now the code in the worker thread no longer
accesses any objects with handles in the UI thread.
When should Invoke be used ?
You don't have to decide yourself if you need to invoke the members of
a control. The Control class has besides the Invoke method the InvokeRequired
property. By reading that you can decide at run-time how if you need the
Invoke method to access the control. Inside the KeepAlive method you will find
out that InvokeRequired is true for all controls. Inside the FrmCallBack it is
always false. The firing of the timer also accesses the listbox control, there
the InvokeRequired property is false as well.
Suspending and resuming a thread
You can activate and deactivate a thread from another thread using the
Suspend and the Resume methods of the Thread class. I will do this with the
worker thread from buttons
in the main thread.
try { t.Suspend(); }
catch(Exception ex) { reportException(ex); }
After calling the Suspend method the trackbar stops moving. The status
of the thread changes twice, first it will be SuspendRequested which will
very soon change to Suspended. Because the status of the thread is only
checked 1000 times a second there is a good chance that you will not even see
the request. The actual state displayed depends on the state it was in when
calling Suspend. If it was asleep, in state WaitSleepJoin, it will now be
in state WaitSleepJoin, Suspended. If it was Running it will now
be a Suspended state. So you can suspend a sleeping thread and you can suspend a
running thread, the fact that the state is a flags enumeration makes it possible
to mark this combination of states.
Calling the Resume method will set the trackbar in motion again.
try { t.Resume(); }
catch(Exception ex) { reportException(ex); }
When resumed the thread will fall back to the state is was before Suspend was called.
Suspend is a forgiving method, calling it again on a thread which is already
suspended has no effect. But Resume is far more critical, calling it on a non
suspended thread will throw an exception in the calling thread. That will be the
main thread, not the thread object which Resume's method was called.

Interrupting and joining a thread
A thread which has been sent to sleep can be woken with the Interrupt
method.
try { t.Interrupt(); }
catch(Exception ex) { reportException(ex); }
When the thread really was asleep this will lead to an exception in the
thread being interrupted. This way the thread is notified that it is woken.

If the thread was already running, nothing happens.
If one thread has to wait for another thread before it can continue, it
should call the the Join method on that thread. Which will pause the
calling thread and pass control to that other thread. The Join method has several overloads,
the one I use here takes as a parameter the maximum amount of time the calling
thread will wait.
try {
t.Join(trackBar2.Value * 100); }
catch(Exception ex) { reportException(ex); }
Here I am making a call from the main UI thread. Pausing this thread will freeze the
form. In the demo you can see this very clearly by setting the second trackbar to a high
value, which
corresponds to 8 seconds. Clicking the join button will result in a 8 second
freeze of the main form.
Stopping a thread
The KeepAlive method has a loop which will run on forever. The only way to
stop it is by killing the thread. This is done by calling the thread's Abort
method
try { t.Abort(); }
catch(Exception ex) { reportException(ex); }
This will raise an exception in the thread which cannot be recovered from.
The thread state will go from AbortRequested to Stopped. Once
stopped the thread cannot be restarted, I will have to create a new thread
object.
A thread which is in Suspended state cannot be successfully aborted.
The calling thread will throw an exception and the thread will get stuck in an AbortRequested
state. From which no successful recovery seems possible.
Stopping the application
When the form is closed the main UI thread will stop running. But this does not
mean that the second worker thread will stop as well. There will no longer be any
live reference to the thread object, so eventually the garbage
collector will destroy the object. But until that moment the thread
will stay alive in an unstable state, working with a trackbar that has gone.
When you run the application from Visual Studio you will see that VS thinks the
app is still running after closing its main form. If you send a break to the app
VS will direct you to the KeepAlive method, that code is still running.
This means that the application has to clean up all threads before it can
close.
By convention the Dispose method is the place to clean up all resources
used, here I can Abort the thread.
protected override
void Dispose( bool
disposing )
{
if(
disposing )
{
if
((t.ThreadState & ThreadState.Suspended) != 0)
t.Resume();
t.Abort();
if
(components != null)
{
components.Dispose();
}
}
base.Dispose(
disposing );
}
The Abort method will finish the thread. A suspended thread
first has to be resumed before it can successfully be aborted.
Where are we ?
In this article I have examined the Thread class in .NET and its main
members. Not all methods do the same thing as their name might suggest and some
methods are far more critical in their usage then others. Starting a new thread
is very easy, ending a thread has a couple of pitfalls. But when you know how it
all works threading is powerful. In the demo
application included you can try for yourself.
What's next ?
The .NET support for threading goes far beyond the material covered here. The
methods provide many overloads to fine-tune their behavior. The System.Threading
namespace has many more types, including support for thread-pooling and advanced
thread synchronization.
|