|
In
a broad sense, multithreading can be thought of as the ability
to perform several jobs simultaneously. For example, while
working in Windows, we can simultaneously carry out different
jobs like printing a document on the printer, receiving e-mails,
downloading files and compiling programs. All these operations
are carried out through different programs, which execute
concurrently in memory.
Even though it may appear that several tasks are being performed
by the processor simultaneously, in actuality it is not so.
This is because in a single-processor environment, the processor
divides the execution time equally among all the running threads.
Thus each thread gets the processors attention in a
round-robin manner. Once the time-slice allocated for a thread
expires, its state and values are recorded, the operation
that it is currently performing is put on hold and the processor
directs its attention to the next thread. Thus at any given
moment if we take a snapshot of memory we will find that only
one thread is being executed by the processor. Thread switching
happens so fast that we get a false impression that the processor
is executing several threads simultaneously.
Multithreading has several advantages to offer. These are
listed below:
Responsiveness: Take the example of MS-Word. Here,
had the spell checker and the grammar checker not run as different
threads, we would have been required to write the document
and then submit it to each checker later. This would have
resulted in low responsiveness. But since the checkers run
in different threads, our document gets checked as we type,
thereby increasing the responsiveness of the application.
Organisation: Threading simplifies program organisation.
In the File Copy example if both the operationsplaying
the animation and the actual copying were run in the same
thread, then after copying a few thousand bytes we would be
required to play the next frame of animation. If we run the
copying code and animation code in separate threads we can
avoid cluttering the copying code with animation code and
vice versa.
Performance: Many a time it happens that a program
needs to wait for user input or has to give some output. The
I/O devices are generally slower than the processor. So the
application waits for the I/O operation to finish first. If
instead, we use another thread for the I/O operation, the
processor time can be allotted to other important tasks independent
of the I/O operation, thereby increasing the performance.
Launching threads
Now that we know what threads are, let us now see how they
can be created. Starting a thread is very simple. The following
statements launch a new thread.
ThreadStart ts = new ThreadStart (func);
Thread t1 = new Thread();
t1.Start();
While creating a thread, we must specify which method should
get executed when the thread starts execution. If we wish
that a static method func( ) should get executed in the thread,
we would have to wrap its address in a delegate and pass it
to the constructor of the Thread class. For this, we have
to use a predefined .NET delegate class called ThreadStart.
The signature of this delegate is such that it returns a void
and does not accept any arguments. Hence func( ) also needs
to have the same signature. To start the thread we must call
the Start( ) method of the Thread class. Both the Thread class
and the ThreadStart delegate belong to the System.Threading
namespace hence we need to write using System.Threading at
the beginning of any program.
Let
us now see an example where multithreading is actually needed.
Here we plan to make a simple game. The UI of the game is
given in the adjacent screen shot:
This application consists of two buttons named start and hit,
and three textboxes named num1, num2 and num3.
The user needs to click on the start button to start the game.
As soon as this button is clicked, random numbers would get
displayed on the three textboxes. Now at any instance if the
user clicks on the hit button, the display of numbers stops.
If the numbers turn out to be the same the user hits the jackpot.
It is impossible to generate and display the random numbers
in the three textboxes simultaneously in the main thread.
If we attempt to do so, numbers in the second textbox would
get generated only after generating and displaying numbers
in the first textbox. So here if we want concurrent functionality,
we need to generate and display the numbers for the textboxes
in three different threads. On clicking the start button,
the three threads get launched as shown in the following code
snippet:
private void start_Click(object sender, System.EventArgs e)
{
t1 = new Thread (new ThreadStart (this.display1));
t1.Start();
t2 = new Thread (new ThreadStart (this.display2));
t2.Start();
t3 = new Thread (new ThreadStart (this.display3));
t3.Start();
}
We have added the references t1, t2 and t3 of the Thread class
as data members of the Form1 class. The display1( ) method
is given below.
public void display1()
{
int j = r.Next (0, 50);
int k = r.Next (50, 100);
for (int i = j ; i <= k ; i++)
{
num1.Text = i.ToString();
Thread.Sleep(200);
}
}
Here we have used an object of the Random class referred to
by r that we also added as a data member of the Form1 class.
Using this object we have created two random numbers and displayed
all numbers lying between the two in the textbox. The display2(
) and display3( ) methods are on the same lines. Hence random
numbers keep on getting displayed simultaneously on the three
textboxes. Now the user needs to click the hit button. The
handler for the hit button is given below:
private void hit_Click(object sender, System.EventArgs e)
{
t1.Abort();
t2.Abort();
t3.Abort();
if (int.Parse (num1.Text) == int.Parse(num2.Text) &&
int.Parse(num2.Text) == int.Parse(num3.Text))
MessageBox.Show( Jackpot );
}
Here we have aborted the threads using the Abort( ) method,
compared the numbers and displayed a message if they are same.
Thread states
From the time a thread is started until it gets terminated
the thread undergoes many transitions and is said to be in
at least one of the several thread states.
A thread can be in one or more of the following states at
any given time: Unstarted, Running, Suspended, WaitSleepJoin,
SuspendRequested, Aborted, AbortRequested, Stopped and StopRequested.
All these states are members of the ThreadState enumeration
declared in System.Threading namespace. Let us now discuss
these states.
Unstarted: A thread is said to be in the Unstarted
state when it is first created. It remains in this state until
the program calls the Start() method of the Thread class.
Running: As soon as the Start() method is called, the
thread starts executing and is said to be in the Running state.
This means that the thread will now start getting CPU time
slots.
Suspended: When a running threads Suspend() method
is called, the thread goes into the Suspended state and would
stop receiving CPU time-slots.
WaitSleepJoin: At times we may want to prevent our
thread from getting CPU cycles till passage of some time or
make our thread wait till some other thread completes some
operation or wait till some other thread terminates.
We
can achieve these three scenarios by calling the methods Thread.Sleep(
), Monitor.Wait( ) and Thread.Join( ) methods respectively
on our thread. Upon calling one of these three methods, our
thread enters the WaitSleepJoin state.
Once
in this state we can transition out of it in the following
three ways:
(a) The sleeping time specified by the Sleep() method expires.
(b) Another thread calls the Thread.Interrupt() method before
the sleeping time expires.
(c) Another thread calls the Monitor.Pulse() method or Monitor.PulseAll()
method. Monitor.Pulse() moves the waiting thread into the
Running state. Monitor.PulseAll() method moves the thread
along with all the other waiting threads into the Running
state.
All the above three ways move the thread from the WaitSleepJoin
state to the Running state.
Stopped:
When a thread completes its execution, it goes into the Stopped
state.
Aborted: Whenever an exception is thrown and the thread
stops executing abruptly it goes into the Aborted state.
AbortRequested: The AbortRequested state is reached
when a thread is in another state but is being requested to
abort. As soon as it comes out of the present state, it gets
aborted.
SuspendRequested: The SuspendRequested state is reached
when a thread is in another state but is being requested to
suspend using the Suspend( ) method. As soon as it comes out
of the present state, it gets suspended.
StopRequested: The StopRequested state is reached when
a thread is being requested to stop. There is no method available
that we can invoke to manually force our thread in this state.
 |
Yashavant
Kanetkar, one of the first Express Computer columnists,
is an established software expert, speaker and author
with several best-sellers to his credit, including titles
like “Let Us C” and the “Fundas” series. Contact him at
kanet@nagpur.dot.net.in |
|