Sei sulla pagina 1di 45

Threads, Events and Mutexes

Threads operate on the premise that the computer is definitely more expeditious than a
human at executing tasks, resulting in the computer idling away most of its processing
time, waiting for the human operator. Threads are a means of overcoming this wastage
of processing time. They perform multiple tasks on a computer in rapid succession,
thereby, creating the illusion of these tasks being executed simultaneously. No
application can ever be comprehensive without employing threads. Before we try to infer
what a thread does in the programming context, let us rummage through a few
examples given below.

Without being discursive, let us venture out on our odyssey of understanding the
concept of a thread with the assistance of a very diminutive program.

a.cs
using System.Threading;
public class yyy
{
public static void abc()
{
System.Console.WriteLine("Hi");
}
}
public class zzz
{
public static void Main()
{
ThreadStart ts = new ThreadStart(yyy.abc);
Thread t = new Thread(ts);
System.Console.WriteLine("Before Start");
t.Start();
}
}

Output
Before Start
Hi

This one is bound to leave you astonished because, we had earlier talked about starting
out with a 'dimunitive' program. However, by no stretch of the imagination can the
above program qualify as miniscule. Besides, the only work accomplished by this
function is that it calls the static function abc, which in turn displays 'Hi'.

In Main, we create an object ts, which is an instance of the class ThreadStart, which is
derived from Delegate. Therefore, even though ThreadStart is a class, it also happens to
be a delegate, whose constructor is given a static function called abc. Function abc is
placed in the yyy class so that other classes can also use it. The program will work in a
similar manner even if the static function is placed in class zzz.

Next, we create another object t, which is an instance of Thread. The constructor of this
object is given a ThreadStart object ts. Indirectly, ts stands for the static function
yyy.abc since it is a delegate.

So far, tranquility prevails and nothing transpires. The function yyy.abc too does not get
called. But, as soon as we call Start off the thread, the function abc gets catapulted into
action. Thus, the function abc is called only when the Start function is called. This is
really no big deal. Note that classes beginning with Thread belong to the
System.Threading namespace.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("Hi");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
}
}

Output
Hi

The above program is similar to the previous one and resembles the samples supplied
by Microsoft. The function abc to be called, is non-static and hence, an object name is
needed to reference it. The ThreadStart delegate object is directly passed as a parameter
to the Thread constructor.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3;i++)
{
System.Console.Write(i + " ");
}
}
public void pqr()
{
for ( int i = 0; i<=3;i++)
{
System.Console.Write(i+ "...");
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.pqr));
t.Start();
t1.Start();
}
}

Output
0 1 2 3 0...1...2...3...

The example embodies a very large amount of code. However, it does not create any
fresh ripples in our pond of knowledge. We have merely created two Thread objects t
and t1, and passed their constructors a different delegate or function name, i.e. abc and
pqr, respectively. Thereafter, the Start function has been called in t and t1. Here, the
function abc gets called, which displays four numbers. Thereafter, the function pqr gets
called, which also displays four numbers, but with three dots.

You may wonder with trepidation as to when you will bite into the real meat. Keep your
impatience in abeyance for a little while !

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3;i++)
{
System.Console.Write(i + " ");
Thread.Sleep(1);
}
}
public void pqr()
{
for ( int i = 0; i<=3;i++)
{
System.Console.Write(i+ "...");
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.pqr));
t.Start();
t1.Start();
}
}

Output
0 0...1 1...2 2...3 3...
Now, we are at the threshold of excitement. By adding the function Sleep, which is a
static function in the Thread class, the functions abc and pqr get called simultaneously,
although not sequentially. This is how the concept of a thread is implemented, i.e. it
empowers your computer to accomplish multiple jobs at the same time. This process is
also termed as multi-tasking.

Take the case of Microsoft Word. When you attempt to save a file, irrespective of the size
of the file, Word appears to consume the same amount of time in completing this task.
To expect it to save a 10MB file within a very short time period is a thought fraught with
absurdity and contrary to reason. Actually, what the Word program does is that it
creates a thread and then solicits the thread to save the file. While the thread is saving
that file, another thread waits for the user to type something. Hence, the user is
emancipated to continue working with Word. Thus, in effect, two tasks are being
executed concurrently without the user being cognizant of it ! The same mechanism is
employed by Excel and other similar products.

Any application under Windows runs in its own thread. Thus, if two programs are
launched, there will be two threads running. The operating system refrains from playing
favorites and gives an equal amount of time to each thread to execute. If there are two
threads co-existing, then, out of every minute of processing time available, each of them
will be allotted 30 seconds to execute. If a third program is now executed, a new thread
will be launched and each of the three threads will be allotted only 20 seconds of the
processor time per minute to execute.

If, instead of running a third program, what if the second program itself creates a
thread ? As before, this will result in the creation of a third thread and each thread will
be allotted 20 seconds of time. The resultant effect would be that that the first program
will be allotted 20 seconds of time, whereas the second program will get 40 seconds per
minute of the processor time. Thus, the larger number of threads that a program
creates, more will be the processor time allotted to it. If you crave for more time and
attention from your computer, desist from throwing a tantrum. Instead, generate
greater number of threads.

The computer allots a specific amount of time to each thread and then puts it to sleep.
Thereafter, it executes another thread. The time given to each thread to execute its code
is designated as a Time Slice. This allocation of Time Slices occurs so swiftly that each
thread suffers from the hallucination that it enjoys the undivided attention of the
computer.

The static function Sleep in the Thread class facilitates this process. Sleep is like the
sandman. It puts the thread to sleep for a certain number of milliseconds, as specified
in its parameter. In this case, it is 1 millisecond. In the earlier example, in a single time
slice, the Thread executed all the code and the function completed execution. In this
case, the Sleep delays it long enough for the next thread to execute, and thus, the code
in the functions get called one after the other.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3;i++)
{
Thread t2 = Thread.CurrentThread;
System.Console.Write(i + "." + t2.Name + " ");
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Name="One";
t1.Name="Two";
t.Start();
t1.Start();
}
}

Output
0.One 0.Two 1.One 1.Two 2.One 2.Two 3.One 3.Two

Here, we have provided mental catharsis by simplifying the program. What we actually
have done is given the same function name abc to our delegate. We have also used the
property Name of the Thread class to assign a distinct name to each thread, i.e. One
and Two respectively. Under normal circumstances, the names assigned are pre-
ordained by the system. Now, both the threads will call the same function abc. How do
we determine as to which thread is calling the function abc?

The Thread class can have members, which are either static or instance. CurrentThread
is a static read-only property that returns a thread object, which represents the thread
that has currently called the function. Here, Thread t2 will either represent t or t1, and
the property Name will display the name of the thread executing the function.

a.cs
using System.Threading;
public class zzz : Thread
{
}

Compiler Error
a.cs(2,14): error CS0509: 'zzz' : cannot inherit from sealed class
'System.Threading.Thread'

The Thread class is a sealed class, therefore we cannot derive from it. The designers of
the Thread class at Microsoft have held very doctrinaire opinions, in that, they believe
that they have envisaged all the possible features required in this class. Thus, they have
not provided any facility to override or modify it. Therefore, we are constrained to use
the Thread class exactly as provided, without introducing any code that will
complement or add to or subtract from the code of the Thread class. For your
information, the Thread class in turn is derived from the interface ISerializable.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("Hi");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
System.Console.WriteLine(t.IsAlive);
t.Start();
System.Console.WriteLine(t.IsAlive);
t.Abort();
System.Console.WriteLine(t.IsAlive);
}
}

Output
False
True
Hi
False

The Thread class has a property called IsAlive that reveals the current state of the
Thread. When we create an object that looks like Thread, the thread is in a dead state
and hence, the IsAlive property has a value of False. Start breathes life into the thread
and executes through the delegate abc. The thread now comes alive, but the code in the
thread function gets executed only after the subsequent WriteLine function, which
displays the value of IsAlive as True. When we stop or Abort the Thread, the thread dies
and the value of the property IsAlive reverts back to False. Thus, IsAlive can hold only
one of the two values, False for dead or True for alive.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("Hi");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
while ( !t.IsAlive )
System.Console.WriteLine("hi1");
}
}

Output
Hi
In today's world, we are working on extremely fast machines. When we execute t.Start(),
the thread is brought to life in a fraction of a second. Hence, the while loop is not
executed because the condition becomes false immediately. Had the machine been
slower, the while loop may have got called a few times, as it would have taken some
time before the thread could have been brought to life. Thus, the speed of execution
may vary, depending upon the speed of the machine. Whenever the sample code creates
a thread, it always contains this while loop.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3 ; i++)
System.Console.Write("Hi " + i + " ");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
System.Console.WriteLine("Over");
}
}

Output
Over
Hi 0 Hi 1 Hi 2 Hi 3

The executable, a.exe, runs in its own thread. So, before calling the function Start, we
already have one thread running. On calling the function Start, two threads run
concurrently, independent of each other. The second thread executes the function abc
independent of the first thread. The first thread executes the last WriteLine function
and then stops, whereas the second thread continues executing the function abc till
there is no more code to call. If, on your machine, the first thread's time slice gets over
before executing the WriteLine function, then some code of the function abc may get
executed before Over gets displayed in the function Main. It is reiterated yet again that,
in case of threads, there can be no guarantee as to when the time slice of a thread will
get over. Even the operating system is not sagacious enough to offer any guarantees in
this regard.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3 ; i++)
System.Console.Write("Hi " + i + " ");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
t.Join();
System.Console.WriteLine("Over");
}
}
Output
Hi 0 Hi 1 Hi 2 Hi 3 Over

The function Join is a blocking function. It makes the program linger at the function
Join till the thread finishes execution. Any code after the Join, in this case the
WriteLine function, will be executed only after the thread dies. A blocking function will
wait until the purpose it is waiting for has reached fructification. Thus, if we want to
wait for a thread to complete execution, we use a Join. Join behaves like a party host,
who is always the last to leave.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( ; ; ) ;
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
bool b = t.Join(10000);
System.Console.WriteLine("Over " + b);
}
}

Output
Over False

The Join function accepts a number as a parameter, which represents the maximum
duration in milliseconds that it should wait for the thread to complete execution. In our
example, the function abc will never end. Thus, the thread t will go on forever.
Therefore, the Join function waits for 10 seconds, gives up and finally returns a False.
Therefore, we are empowered to decide the duration for which we want to wait for the
thread to complete execution. Let us not forget that our application has not yet
terminated and is still hanging around in memory. At this stage, if you press Ctrl-Alt-
Del, you will see a list of programs running. Now, select the End Task option for the
program called 'a'. An alternative approach could be to add the Abort function t.Abort()
after the WriteLine function.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( ; ; ) ;
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Join();
}
}

Output
Unhandled Exception: System.Threading.ThreadStateException: Thread has not
been started.
at System.Threading.Thread.Join()
at zzz.Main()

If there is no thread running, the Join function will throw the exception of
ThreadStateException.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
Thread.Sleep(3);
t.Abort();
System.Console.WriteLine("Over");
Thread.Sleep(100);
t.Start();
}
}

Output
abc
Over
Unhandled Exception: System.Threading.ThreadStateException: Thread is running
or terminated. Cannot restart.
at System.Threading.Thread.StartInternal(IPrincipal principal, StackCrawlMark &
stackMark)
at System.Threading.Thread.Start()
at zzz.Main()

At first, we start the thread t by calling the Start function. Thereafter, we make the
main thread sleep for a little while. Then, we abort the thread and again sleep for a little
while, in order to enable the thread to fulfill its last wishes and finally die. Now that the
thread is dead, we try and infuse life into it by calling Start again. A thread, which has
died cannot be resuscitated. Since we have had the audacity to attempt this, the results
are nearly cataclysmic, resulting in the generation of an exception which has to be
caught.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
Thread.Sleep(3);
t.Abort();
System.Console.WriteLine("Over");
Thread.Sleep(100);
try
{
t.Start();
}
catch (ThreadStateException e)
{
System.Console.WriteLine("In Exception");
}
}
}

Output
abc
Over
In Exception

The exception thrown is ThreadStateException, which needs to be caught in our code.


Otherwise, the runtime message box is displayed to the user.

The Thread class constructor can throw two types of exceptions:-

• ArgumentNullException - When it is called without a delegate in the


constructor.
• SecurityException - When the program does not have permission to create a
thread.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc");
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
System.Console.WriteLine(t.ApartmentState);
t.ApartmentState = ApartmentState.STA;
t.Start();
System.Console.WriteLine(t.ApartmentState);
}
}

Output
Unknown
STA
abc

We all have our own apartments, which are normally done up as per our preferences
i.e. 'We are the kings of our castles'. Threads also have apartments. Any thread can be
asked to execute either in a single-threaded apartment or in a multi-threaded
apartment. These values can be established using the property ApartmentState, either
once at the beginning, or while the thread is running. The various values that
ApartmentState can assume are as follows:
· STA : Single Threaded Apartment.
· MTA : Multi Threaded Apartment.
· Unknown : Default value assigned when no value is set.

We, however, cannot use numbers for a string. Thus, an enum called ApartmentState
has been used. It holds three values i.e. 0, 1 and 2 corresponding to STA, MTA and
Unknown respectively.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
System.Console.WriteLine("abc " + Thread.CurrentThread.IsBackground);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
System.Console.WriteLine("Main " + Thread.CurrentThread.IsBackground);
t.Start();
}
}

Output
Main False
abc False

A thread executes either in the background or in the foreground. The property


IsBackground indicates the mode in which the thread will run. An important point to be
noted here is that, a thread which executes in the background automatically shuts
down when its main program quits.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0 ; i<=100; i++)
{
System.Console.WriteLine("abc " + i);
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
System.Console.WriteLine("Main " + Thread.CurrentThread.IsBackground);
t.IsBackground = true;
t.Start();
Thread.Sleep(10);
}
}

Output (t.IsBackground = true)


Main False
abc 0

Output (t.IsBackground = false)


Main False
abc 0
abc 1
abc 2
..
..
abc 100
When you change the true to false in t.IsBackground, the output shows the code of
function abc being executed. The for loop displays values from 0 to 100.

By changing the property IsBackground of a thread to true, the thread terminates when
the main program stops. Thus, the for loop does not have the time to display values
upto 100. At times, the program displays abc with a value of 0.

The sleeps are used to demonstrate the effect of the first thread shutting down and the
second carrying on execution. On changing the property IsBackground to false, the
thread takes its own sweet time to execute its code without any regard for the first
thread, which may or may not have completed its job. So, the foreground threads
behave exactly in the same manner as the background threads, when the property is
initialized to true.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=10;i++)
{
System.Console.Write(i + " ");
Thread.Sleep(1);
}
}
public void pqr()
{
for ( int i = 0; i<=10;i++)
{
System.Console.Write(i+ "...");
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.pqr));
System.Console.WriteLine(t.Priority);
t.Priority = ThreadPriority.Highest;
t1.Priority = ThreadPriority.Lowest;
t.Start();
t1.Start();
}
}

Output
Normal
0 1 2 3 4 0...5 1...6 2...7 3...8 4...9 5...10 6...7...8...9...10...

We are at liberty to decide the duration of the time-slice to be allotted to our thread vis-
a-vis other threads. This can be achieved by setting the Priority property accordingly.
By default, the priority level is 2. Hence, we see 'Normal' displayed as the output. All
threads start at this priority level. The ThreadPriority enum comprises of five levels of
priority, which are as follows:

• 0 - Zero
• 1 - BelowNormal
• 2 - Normal
• 3 - AboveNormal
• 4 - Highest

In our program, we have changed the priority of thread t to the highest possible and
that of thread t1 to the lowest. As a result, Thread t is accorded more time than thread
t1, and it finishes its for loop much before thread t1 can do so.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
try
{
System.Console.WriteLine("in abc");
for ( ; ; ) ;
}
catch ( System.Exception e)
{
System.Console.WriteLine("in abc Exception " +e.ToString() );
}
finally
{
System.Console.WriteLine("in abc Finally");
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
Thread.Sleep(10);
t.Abort();
}
}

Output
in abc
in abc Exception System.Threading.ThreadAbortException: Thread was being
aborted.
at yyy.abc()
in abc Finally

Aborting a Thread while it is busy executing some code generates an exception. Thus,
the function abc will be sent an exception, resulting in a call to the 'catch' and 'finally'
clauses. The thread will be terminated after executing the code in the Catch and the
Finally clause.
a.cs
using System.Threading;
public class yyy
{
public void abc()
{
try
{
System.Console.WriteLine("in abc");
for ( ; ; ) ;
}
catch ( System.Exception e)
{
System.Console.WriteLine("in abc Exception " +e.ToString() );
}
finally
{
System.Console.WriteLine("in abc Finally");
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Abort();
t.Start();
}
}

Output
(no output)

Aborting a thread before it has started execution does not generate any exceptions,
since the thread is in a dead state. Hence, no code in abc ever gets called. This situation
is akin to the one where you don your best attire to watch cricket live at the stadium,
but the show gets ruined due to heavy downpour, resulting in the match being
abandoned without a ball being bowled !

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for (int i = 1 ; i<= 10 ; i++ )
{
System.Console.Write(i + " " );
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
Thread.Sleep(10);
t.Suspend();
System.Console.WriteLine("\n After Suspend");
Thread.Sleep(10);
System.Console.WriteLine("Before Resume");
t.Resume();
}
}

Output
1
After Suspend
Before Resume
2 3 4 5 6 7 8 9 10

We are allowed to Suspend a thread executing its code, at any point in time. In the
above program, we let the thread run for a little while and then Suspend it. Thereafter,
we Resume it. Thus, the thread is like a puppet in our hands. We can decide when and
which strings to pull and the thread does our biding. If the thread has already been
suspended, then Suspending it once again has no effect, and thus, it does not result in
an error. A single Resume is enough to undo a million Suspends.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
for ( int i = 0; i<=3;i++)
{
Thread t2 = Thread.CurrentThread;
System.Console.Write(i + " " + t2.GetHashCode() + " ");
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
0203121322233233

Every thread is assigned a number internally. This number is called the HashCode.
Hashing is the process of assigning a number to an entity so that it can easily be
located from a long list. This hash number is useful and is pressed into service when we
are required to keep track of a large number of threads internally.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
Monitor.Enter(this);
for ( int i = 1; i<=5;i++)
{
System.Console.Write(i + " " + Thread.CurrentThread.GetHashCode() + " ");
Thread.Sleep(1000);
}
Monitor.Exit(this);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
12223242521323334353

Let us now clarify the abstract concept of the life of a thread with the help of an
example.

Any resource that many people desire is called a shared resource. Imagine the chaos
that would be caused if five programs tried to simultaneously use a single network
printer for printing, during their respectively time-slices. This would result in the
printer printing a few pages for program 1, then a few pages for program 2, and so on.
The printer will keep printing merrily, but the printed output will all be jumbled up.

Thus, ideally, one program should use a shared resource at a time, till its job is
complete. For this, a method has to be implemented by means of which, unless one
program finishes executing some specific code in a function, no other program should
be allowed to execute it. Thus, all other programs would have to wait until the first
thread finishes execution.
Let us assume that function abc contains code which sends a request to the printer to
print. The thread t gets the first shot at running abc. The second thread t1 has to wait
until thread t finishes execution, even if the duration of executing abc takes more time
than one time slice, due to a large value being assigned as a parameter to the sleep
function. This is achieved by using the static function Enter of the class Monitor.

The function Enter is given an object which refers to the class in which the function
resides. From that point onwards, every thread has to wait at Enter. The barrier lowers
only when Exit is called from the Monitor class. Enter acts as a fussy security guard
who decides as to which thread is to be prohibited and which one is to be permitted to
execute code. No two threads can ever enter the Enter function together. It is a rigid one
way lane from Enter to Exit. One car is allowed in at one time and another car can pass
through only after the first car leaves Exit. You can see that the names Enter and Exit
have been chosen very aptly.

a.cs
using System;
using System.Threading;
public class ggg
{
};
public class yyy
{
public ggg g1;
public void abc( )
{
Monitor.Enter(g1);
for ( int i = 1; i <= 3; i++)
{
Console.Write(" Hell " + i);
Monitor.Wait(g1);
Console.Write(" Waitabc " + i);
Monitor.Pulse(g1);
}
Monitor.Exit(g1);
}
};
public class xxx
{
public ggg g1;
public void pqr( )
{
Monitor.Enter(g1);
for ( int i = 1; i <= 3; i++)
{
Console.WriteLine(" Everyone " + i);
Monitor.Pulse(g1);
Monitor.Wait(g1);
Console.Write(" Waitpqr " + i);
}
Monitor.Exit(g1);
}
};
public class zzz
{
public static void Main(String[] args)
{
ggg g = new ggg( );
yyy a = new yyy( );
a.g1 = g;
Thread t = new Thread(new ThreadStart(a.abc));
t.Start( );
xxx b = new xxx();
b.g1 = g;
Thread t1 = new Thread(new ThreadStart(b.pqr));
t1.Start( );
}
};

Output
Hell 1 Everyone 1
Waitabc 1 Hell 2 Waitpqr 1 Everyone 2
Waitabc 2 Hell 3 Waitpqr 2 Everyone 3
cs Waitabc 3 Waitpqr 3

The above example may be a protracted one, yet it is fascinating.


The world has never been able to work with each other. The only global body we have is
the UNO. People are mostly loners.

In the entry point function Main, we create an object g, which is an instance of a class
ggg, having no code or variable. It embodies the emptiness of space. This is a class that
is incapable of doing any good or bad. We then create two objects that look like yyy and
xxx respectively, and pass them as delegates, so that the threads can call functions abc
and pqr. We also initialize the object g1, an instance of ggg, in each of them to g. This is
crucial. The g1's in yyy and xxx represent the same g that is created in Main.

After initializing the requisite members, we activate the two threads. As earlier, let us
assume that the function abc gets called first.

Monitor.Enter considers taking a handle to any object, which is to be synchronized


between multiple instances of threads, as its first parameter. This function needs an
Exit function to release its handle. Thus, the number of Exits in the program must
correspond to the number of Enters.

It prints "Hell", and then due to the Wait function, it waits indefinitely, till it finally
receives runtime notification. However, the thread accomplishes nothing significant.
Thus, we do not see the Waitabc displayed, since it is entered after the Wait function.

The second thread starts executing the function pqr. Monitor.Enter uses the same
synchronized object, and now it displays Everyone. The next function to be called in pqr
is Monitor.Pulse. Pulse does a very simple thing. It wakes up any thread having the
same handle, which happens to be waiting at a Wait. Thus, the thread in abc wakes up.
However, the one in pqr waits for someone to Pulse it.

By using Wait and Pulse, we can easily play a game of tag. The thread t performs some
work and waits for thread t1 to wrap up whatever work it is preoccupied with. Thread t
does not know how long thread t1 will take to fulfill its side of the bargain. But no
sweat! Thread t waits at a Wait for the thread t1 to Pulse it. Then, thread t1 waits at a
Wait for thread t to do some work. When thread t concludes, it Pulses thread t1, and so
on. This can go on and may involve some other threads too.

The threads use the handle g, which is the synchronized region. It is akin to being an
integral part of a clandestine social group. The parameter to these Monitor functions
clubs them together as a group. If you change the handle, the group breaks up, leaving
your thread in a lurch. The Wait function can also be made to wait for a specified time
period.

If we change the line Monitor.Wait(g1) to Monitor.Wait(g1,0), the output changes


dramatically.

A monitor is associated with an object on demand and it cannot be instantiated at all. If


the constructor is private, we can never create an object. But, the static functions of
that object can be used. The Monitor functions can be called from anywhere since they
are unbound. There are no restrictions on the sources of the object to be called.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
Monitor.Enter(this);
Monitor.Enter(this);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(this);
}
}
public class zzz
{
public static void Main() {
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
12
22
32

The Exit function can be called as often as one desires. In this program, we have two
threads, t and t1. Let us assume that thread t has the first go. At this time, it will pass
through the first Monitor.Enter, as well as the second Monitor.Enter, without waiting at
all. However, thread t1 will have to wait at the first Monitor.Enter, since the thread t
called the Enter function twice, but Exited only once. Thus, the program never quits
out. Had it called Exit twice, the thread t1 too would have woken up.

a.cs
using System.Threading;
public class yyy {
public int j=1;
public void abc()
{
Monitor.Enter(j);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(j);
}
}
public class zzz
{
public static void Main() {
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
}
}

Output
12
22
32
Unhandled Exception: System.Threading.SynchronizationLockException: Exception
of type System.Threading.SynchronizationLockException was thrown.
at yyy.abc()

Monitor.Enter takes an object, and not a variable, as a parameter. This is the reason
why we used this parameter or some reference object, in the earlier programs. If a
variable or a value object is used, an exception will be thrown. Thus, value types or null
objects are not allowed as parameters to Monitor.Enter.

a.cs
using System.Threading;
public class xxx
{
}
public class ttt
{
}
public class yyy
{
static int p = 1;
xxx x = new xxx();
ttt t = new ttt();
public void abc()
{
if ( p == 1)
{
Monitor.Enter(x);
p++;
}
else
Monitor.Enter(t);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(x);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
12
13
22
23
32
33

The Enter function must be used with some caution. The variable p initially has a value
of 1. The first thread t sees the value of p as 1. Hence, the 'if' statement is true. Thus, it
crosses Monitor.Enter with x as a parameter. It then takes a snooze at the Sleep
function, while the second thread t1 executes function abc, where it sees the value of
variable p as 2. This thread now meets the Monitor.Enter function, with t as the
parameter. The object reference given to this Enter is entirely at variance with the object
given to the earlier one. Thus, both the threads execute the Enter function, which fails
the very objective of the Enter function. For Enter to work as expected, it should be
provided with the same object as a parameter to itself.

The concept of a Lock is normally used to explain the Monitor class. One thread gets a
Lock, while the others wait until the lock is released.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(this);
}
}
public class zzz
{
public static void Main() {
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
True
False
12
1 3
2 2
2 3
3 2
3 3

The TryEnter function is similar to the Enter function, but it does not block. If the
thread enters successfully, TryEnter returns a true. This is what happens the first time.
But in the case of the second thread t, it returns a false, even when it enters the critical
section.

a.cs
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this,1000);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(this);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
True
False
12
13
22
23
32
33

The TryEnter function is overloaded to accept a number as a parameter, which


represents the time duration for which the TryEnter function should block or wait. In
our case, the timeout exceeds the time that thread t spends in the function. Hence, we
get a value of False. If we add an extra zero to the time parameter, it will return a true
instead. If the value of the parameter is infinite, the behaviour of the TryEnter becomes
akin to that of Enter.
Thread Attributes

a.cs
using System.Threading;
public class yyy
{
[System.ThreadStaticAttribute ()]
public static int j = 1;
public static int k = 1;
public void abc()
{
for ( int i = 1; i<=3;i++)
{
Thread t2 = Thread.CurrentThread;
j++;k++;
System.Console.Write(i + "." + t2.Name + " j=" + j + " k=" + k + " ");
Thread.Sleep(1);
}
}
}
public class zzz
{
public static void Main() {
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Name="One";
t1.Name="Two";
t.Start();
t1.Start();
}
}
Output
1.One j=1 k=2 1.Two j=1 k=3 2.One j=2 k=4 2.Two j=2 k=5 3.One j=3 k=6 3.Two
j=3 k=7

A static variable is a loner, as reiterated by us on numerous occasions. It belongs to the


class and not to an object. Thus, there will always be a single static variable.

Let us consider a case where we have two threads t and t1 executing code in the same
function abc, that also contains static variables j and k. The first question that strikes
us is 'Will each of the threads see the same static variable or a different one?' Also, if we
assume that both threads cater to the same static variable, when one thread
increments the variable, will the other thread see the new value? And if not, then, is the
compiler creating a separate static variable for each thread?

Too many questions that need answers!

We want to possess the best of both the worlds. At times, we need the threads to work
on the same static variable, while at other times, we need the threads to access
separate copies of the static variable. The default behaviour is that of the static variable
being shared. Thus, the variable k is incremented by both the threads, and each thread
sees the same value. The value of k finally reaches 7 at the end.

The variable j has an attribute ThreadStaticAttribute above it. This attribute may have a
fancy name, but it merely creates a separate variable for each thread executing the
function. Thus, we have two static variables j, one for each thread t and t1. Any
changes made to the value of the variable j by t, does not get reflected in the static
variable j of the thread t1.

1.One j=2 k=2 1.Two j=3 k=3 2.Two j=4 k=4 2.One j=5 k=5 3.Two j=6 k=6 3.One j=7 k=7

The above attribute only acts on static variables. We see the above output when we
remove static from the variables j and k. The compiler is very clear. The attribute
ThreadStaticAttribute is only to be used on static variables, but when applied on non-
static variables, the compiler does not generate any error or warning. It simply ignores
the attribute. Thus, instance variables are shared across threads, whereas, local
variables like i are independent of threads. Thus, a variable can either be shared across
threads or may be independent of the thread. No in-between options are allowed.

Events

a.cs
using System.Threading;
public class zzz
{
public static void Main() {
ManualResetEvent a;
a = new ManualResetEvent(false) ;
System.Console.WriteLine("Before WaitOne " );
bool b = a.WaitOne(1000,false);
System.Console.WriteLine("After WaitOne " + b);
}
}

Output
Before WaitOne
After WaitOne False

We have created an object a that is an instance of a ManualResetEvent class. The


constructor is assigned a value of false. Then, we call a function WaitOne from this
class with a number as the first parameter, and a boolean value as the second
parameter. This number, 1000, is the number of milliseconds for which we want the
thread to wait at the function WaitOne. The thread therefore waits for one second before
quitting out, and then returns a False. Had we used the enum Timeout.Infinite that has
a value of -1, we could have made the function wait forever. Thus, we can keep a thread
waiting either for a specified duration of time or forever.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a;
a = new ManualResetEvent(true) ;
System.Console.WriteLine("Before WaitOne " );
bool b = a.WaitOne(1000,false);
System.Console.WriteLine("After WaitOne " + b);
}
}
Output
Before WaitOne
After WaitOne True

By changing the value in the constructor to true, the thread just refuses to wait for
anyone. It just whizzes past, without even acknowledging the presence of the function
WaitOne. Further, the return value of WaitOne i.e. b returns a True.

Let us delve a little deeper into this mystery. A ManualResetEvent is like a boolean
variable. It can possess only one of the two values, true or false. When we first created
such an object, we gave it a value of false. So, the function WaitOne waited till the
Event object turned into True or the time value expired. Since the time duration got
over while waiting, and the value of the Event object was not set to True, it stopped
blocking and returned with a value of false.

In the next example, the event object already has a value true and hence, there is no
wait. Here, the function WaitOne returns true because the non-blocking in this case,
cannot be attributed to a timeout. Thus, a ManualResetEvent has two states, true or
false; or in technical lingo, signaled or non-signaled. A value of true implies a signaled
state, while a false would mean that it is in the non-signaled state.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a;
a = new ManualResetEvent(true) ;
System.Console.WriteLine("Before WaitOne " );
bool b = a.WaitOne(1000,true);
System.Console.WriteLine("After WaitOne " + b);
b = a.WaitOne(10000,true);
System.Console.WriteLine("After second WaitOne " + b);
}
}

Output
Before WaitOne
After WaitOne True
After second WaitOne True

Since we do not consider ourselves to be very cerebral, we shall desist from using fancy
terminology like signaled or non-signaled. Instead, we shall stick to the simple true and
false. We initially created the object a to be true or signaled (Oops! There we go again!).
It then passed the first and the second WaitOne without waiting. This occurred because
it is a Manual ResetEvent (as the name itself suggests), and not automatic. Once it is
set to true, the event cannot change to false automatically. This has to be accomplished
manually.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a;
a = new ManualResetEvent(true) ;
System.Console.WriteLine("Before WaitOne " );
bool b = a.WaitOne(1000,true);
System.Console.WriteLine("After WaitOne " + b);
a.Reset();
b = a.WaitOne(10000,true);
System.Console.WriteLine("After second WaitOne " + b);
}
}
Output
Before WaitOne
After WaitOne True
After second WaitOne False

The Reset function can be used to change the state from true to false as it changes the
state to false. Hence, it has to wait in queue like a commoner, since the event ignores
the earlier WaitOne. After some time, it gets activated due to a timeout and the function
WaitOne returns a False.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a;
a = new ManualResetEvent(false) ;
System.Console.WriteLine("Before WaitOne " );
a.Set();
bool b = a.WaitOne(1000,true);
System.Console.WriteLine("After WaitOne " + b);
b = a.WaitOne(10000,true);
System.Console.WriteLine("After second WaitOne " + b);
}
}

Output
Before WaitOne
After WaitOne True
After second WaitOne True

Now, despite the fact that that the Event is originally false or non-signaled, the Set
function sets it to true or the signaled state. At this stage, no power on earth is
equipped to stop the event as it surges past the WaitOnes, treating them with disdain. It
is not in the least concerned about stopping-by to pay homage to them. Such arrogance
!

The class ManualResetEvent is a sealed class, like most other classes belonging to the
Thread family. The developers at Microsoft were of the belief that no one would be able
to add any more value to the class, and thus forbade any changes. This class is also
derived from another class called WaitHandle, which in fact, embodies functions like
WaitOne etc. The constructor has to be passed a parameter that affirms the initial state
of the Event object. No default state is available. The Set and Reset methods return a
boolean value, which indicates whether the change has taken place successfully or
otherwise.
WaitHandle is a class from which ManualResetEvent is derived. A class that offers
functions which are polite, is called a synchronization class. People who are polite, wait
for others to finish doing their work, and it is only then that they proceed with their
own work. Any class that waits for another is called a synchronization class. All
synchronization objects derive from WaitHandle, and so does ManualResetEvent.

We are cautioned not to use this class directly, since it is the other classes that use this
class. The documentation very clearly specifies that the CLR i.e. the Common Language
Runtime or code written by Microsoft, calls the constructor of WaitHandle. Therefore, do
not use this class directly. You are being forewarned!

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a,b;
a = new ManualResetEvent(false) ;
b = new ManualResetEvent(true) ;
WaitHandle [] w = new WaitHandle[2];
w[0] = a; w[1] = b;
System.Console.WriteLine("Before WaitAll " );
bool c = WaitHandle.WaitAll(w,10000,true);
System.Console.WriteLine("After WaitAll " + c);
}
}
Output
Before WaitAll
After WaitAll False

We can complicate life as much as we like. Similarly, we can complicate the WaitOne,
by introducing its elder brother, WaitAll. This function will wait for all the event objects
to become true or signaled, or it will stay there till the timeout occurs. In this manner,
life can be made as complicated as we like. Until all the events reach the signaled state,
the WaitAll remains extremely stubborn and does not budge from its vantage position.
It does not take cognizance if a few of the events are signaled. All the events should be
signaled. This could be a number approaching infinity.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a,b;
a = new ManualResetEvent(false) ;
b = new ManualResetEvent(true) ;
WaitHandle [] w = new WaitHandle[2];
w[0] = a; w[1] = b;
System.Console.WriteLine("Before WaitAll " );
bool c = WaitHandle.WaitAll(w);
System.Console.WriteLine("After WaitAll " + c);
}
}
Output
Before WaitAll

The number of functions in the class WaitHandle may be numerous, but most of them
are only helper functions, which make life tranquil. The WaitAll function, if called with a
single parameter, assumes that the second parameter is Timeout.Infinite or -1. These
overloads can be ignored safely. It is the same for the WaitOne method.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a,b;
a = new ManualResetEvent(false) ;
b = new ManualResetEvent(true) ;
WaitHandle [] w = new WaitHandle[2];
w[0] = a; w[1] = a;
System.Console.WriteLine("Before WaitAll " );
bool c = WaitHandle.WaitAll(w);
System.Console.WriteLine("After WaitAll " + c);
}
}

Output
Before WaitAll

Unhandled Exception: System.DuplicateWaitObjectException: Exception of type


System.DuplicateWaitObjectException was thrown.
at System.Threading.WaitHandle.WaitMultiple(WaitHandle[] waitHandles, Int32
millisecondsTimeout, Boolean exitContext, Boolean WaitAll)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles, Int32
millisecondsTimeout, Boolean exitContext)
at System.Threading.WaitHandle.WaitAll(WaitHandle[] waitHandles)
at zzz.Main()

The CLR (Common Language Runtime, if you have forgotten) never sleeps, but the
compiler can be caught dozing at times. C# hates duplicates, and thus, we cannot pass
the same handle to the WaitHandle array. If you attempt to do so, a runtime exception
will be thrown even when there was no reason to throw an exception. The runtime could
have simply ignored the duplicate, but it chose not to.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
ManualResetEvent a,b;
a = new ManualResetEvent(false) ;
b = new ManualResetEvent(true) ;
WaitHandle [] w = new WaitHandle[2];
w[0] = b; w[1] = a;
System.Console.WriteLine("Before WaitAll " );
int c = WaitHandle.WaitAny(w);
System.Console.WriteLine("After WaitAll " + c);
}
}

Output
Before WaitAll
After WaitAll 0

Change line containing w[0] = b; w[1] = a; to w[0] = a; w[1] = b; and the output changes
to the following:-

Output
Before WaitAll
After WaitAll 1

The WaitAny function waits till any of the events change to signaled or true. When this
occurs, it returns the array index of the event which caused the exit. In this case, object
b is in the signaled state, and since it is the first member of the array, the return value
is 0. On interchanging the object to b, which is the second array member, the return
value becomes 1. If both events are in the signaled state, then the lower array index is
returned. If a timeout occurs, the value returned is 258.

AutoResetEvent Class

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
AutoResetEvent a;
a = new AutoResetEvent(false) ;
System.Console.WriteLine("Before WaitAll " );
bool c = a.WaitOne(1000,true);
System.Console.WriteLine("After WaitOne " + c);
}
}

Output
Before WaitAll
After WaitOne False

Let us now move up the value chain. The AutoResetEvent class works in the same way
like a ManualResetEvent class in the above example. It simply waits for the timeout to
take place or the Event to be signaled.

a.cs
using System.Threading;
public class zzz
{
public static void Main() {
AutoResetEvent a;
a = new AutoResetEvent(true) ;
System.Console.WriteLine("Before WaitAll " );
bool c = a.WaitOne(1000,true);
System.Console.WriteLine("After WaitOne " + c);
c = a.WaitOne(10000,true);
System.Console.WriteLine("After WaitOne1 " + c);
}
}

Output
Before WaitAll
After WaitOne True
After WaitOne1 False

Ah! Again the wait. The AutoResetEvent object safely crosses the first WaitOne. But
there is one major variation now in that, the state of the object changes from true to
false or signaled to non-signaled. Thus, it is obliged to pay homage for some time at the
second Wait. The name here is Auto and not Manual. Just as we would change color if
we saw a wild boar charging at us, the AutoResetEvent changes state at the Wait.
Everything else remains identical.

Mutexes

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main()
{
m = new Mutex(true,"vijay");
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
}
public void abc( )
{
System.Console.WriteLine("abc start");
m.WaitOne( );
System.Console.WriteLine("abc end");
}
}

Output
abc start

If the boolean value of true in the Mutex constructor is changed to false, the output will
change to the following:-

Output
abc start
abc end

A mutex is a synchronization object derived from the class WaitHandle. A Mutex is, in
some sense, similar to the other Event classes.
We commence by creating a mutex and assigning it a boolean value of either true or
false. In this context, these values have a slightly different connotation. They decide
whether the creating thread owns the object or not. True allows the thread to own it,
whereas False does not. Thus, with a mutex, we need to become very materialistic and
start taking ownership of things. The current thread owns the mutex while the other
threads are inclined to own it.
True or false, signaled or non-signaled, own it or not, are all the same concepts. The
main thread already owns the mutex, which is named as vijay. The name is optional
and is used mostly to refer to the mutex. Thereafter, we create a thread which calls the
function abc. After displaying a message, the function WaitOne is called. This function
will now stake a claim for ownership of the mutex, but since the main thread owns it,
thread t cannot proceed beyond this function until the original thread gives up
ownership. If we change the True in the constructor to False, then the ownership is not
claimed. Thus, the thread t will not have to wait at the WaitOne as there is no owner.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main()
{
m = new Mutex(true,"vijay");
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
}
public void abc( )
{
System.Console.WriteLine("abc start");
m.ReleaseMutex();
m.WaitOne( );
System.Console.WriteLine("abc end");
}
}
Output
abc start

Unhandled Exception: System.ApplicationException: Attempt to release mutex not


owned by caller.
at System.Threading.Mutex.ReleaseMutexNative(IntPtr handle)
at System.Threading.Mutex.ReleaseMutex()
at zzz.abc()

The main thread becomes the owner of the mutex as the boolean value supplied in the
mutex constructor is True. Then, in the function abc, we try to release the ownership of
the mutex by using the function ReleaseMutex. The thread t does not own the mutex.
Hence, we get a runtime error as only the owner i.e. the main thread, can release the
ownership of a mutex and pass it on to another thread.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main() {
m = new Mutex(true,"vijay");
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
System.Console.WriteLine("Before Thread Sleep");
Thread.Sleep(10000);
System.Console.WriteLine("After Thread Sleep");
m.ReleaseMutex();
System.Console.WriteLine("All over");
}
public void abc( )
{
System.Console.WriteLine("abc start");
m.WaitOne( );
System.Console.WriteLine("abc end");
}
}

Output
Before Thread Sleep
abc start
After Thread Sleep
abc end
All over

Now alls well that ends well. The main thread sleeps for a little while and then calls
ReleaseMutex. This awakens the thread t, and thus, the function abc now quits out.
Remember that you cannot give away what you do not own.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main()
{
m = new Mutex(true,"vijay");
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
System.Console.WriteLine("Before Thread Sleep");
Thread.Sleep(1000);
System.Console.WriteLine("After Thread Sleep");
m.ReleaseMutex();
System.Console.WriteLine("Before WaitOne");
m.WaitOne();
System.Console.WriteLine("All over");
}
public void abc( )
{
System.Console.WriteLine("abc start");
m.WaitOne();
System.Console.WriteLine("abc before sleep");
Thread.Sleep(100000);
System.Console.WriteLine("abc end");
}
}

Output
Before Thread Sleep
abc start
After Thread Sleep
abc before sleep
Before WaitOne

There is a long sleep in the function abc. The mutex is still owned by the thread t, while
the main thread is trying to regain its ownership. It can only gain ownership if the
thread t releases the mutex or dies. Thus, until the thread t dies, we will not see All
Over displayed at all. We could have had another sleep in abc and we could have
released the mutex before that sleep. The choice is ours. Thus, a mutex is fully
concerned with ownership issues. Either you own a mutex or you do not.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
static Mutex n;
public static void Main()
{
m = new Mutex(true,"vijay");
n = new Mutex(true);
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
System.Console.WriteLine("All over");
}
public void abc( )
{
System.Console.WriteLine("abc start");
Mutex [] g = new Mutex[2];
g[0] = m;g[1] = n;
Mutex.WaitAll(g);
System.Console.WriteLine("abc end");
}
}

Output
All over
abc start

The program will hang. This is because we have two mutexses that have to be waited
upon. Both m and n fill up the array g, and since both are true, the function abc will
wait forever. Same rules of event classes apply to a mutex object.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main()
{
m = new Mutex(true,"vijay");
zzz a=new zzz( );
Thread t= new Thread(new ThreadStart(a.abc));
t.Start();
System.Console.WriteLine("Before Thread Sleep");
Thread.Sleep(1000);
System.Console.WriteLine("After Thread Sleep");
m.ReleaseMutex();
System.Console.WriteLine("Before WaitOne");
m.WaitOne();
System.Console.WriteLine("All over");
}
public void abc( )
{
System.Console.WriteLine("abc start");
m.WaitOne();
System.Console.WriteLine("abc after Wait");
m.WaitOne();
System.Console.WriteLine("abc after second Wait");
Thread.Sleep(10000);
System.Console.WriteLine("End");
}
}

Output
Before Thread Sleep
abc start
After Thread Sleep
abc after Wait
abc after second Wait
Before WaitOne
End
All over

The same rules about mutex ownership as mentioned earlier are discussed here. There
may be two WaitOne functions, but the first WaitOne that gets the ownership, retains
it. Thus, there was no wait between the first and the second wait. The concept of a
mutex is extremely simple. A particular thread owns it. It can cross a wait without
waiting and only when it releases the mutex can another thread claim and possess its
ownership. Other threads cannot own it and they will have to wait at a wait. Dual
ownership is not allowed.

a.cs
using System.Threading;
public class zzz
{
static Mutex m;
public static void Main()
{
m = new Mutex(false,"vijay");
zzz a=new zzz( );
System.Console.WriteLine("abc start");
m.WaitOne( );
System.Console.WriteLine("abc end");
}
}

Output
abc start
abc end
A Mutex only works with multiple threads. So, using a mutex with a single thread is
futile, because a mutex is concerned with determining as to which thread owns it.
Therefore, there must be at least two threads.

Timers

a.cs
using System.Threading;
public class zzz
{
public static void Main() {
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100,0);
Thread.Sleep(1000);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
hell

Timers are time-related. We have created a timer object and have passed it 4 values in
its constructor. The first parameter is the delegate object which represents the function
that should be called after a certain interval elapses. The second parameter is the object
to be passed to the timer function. The third and fourth parameters are numbers
representing time i.e. time and period. If the thread is not asked to sleep, the main
thread will quit out and there will be no thread running. The entire program will halt
and all the unfired timers will die.

Thus, if we remove the last Sleep, the word 'hell' does not get displayed. The timer
delegate has to receive an object as a parameter since it is part of the delegate
signature. The timer also has to wait for a specified time which is stated in the
constructor, before it can call the function. This wait is accomplished using a thread in
a thread pool. Needless to say, the Timer class is a sealed class.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100,0);
t.Dispose();
Thread.Sleep(1000);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
(none)

If we ever desire to cancel the timer for whatever reasons, we can call the Dispose
method to kill the timer. The timer dies and the function to be called does not get called
at all.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
ManualResetEvent e = new ManualResetEvent(false);
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,10000,300);
t.Dispose(e);
e.WaitOne();
System.Console.WriteLine("All Over");
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
All Over

The Dispose function can also take a parameter which is an event that gets signaled
when the timer dies. Thus, the program that would have waited indefinitely now quits
out because, removing the timer from the timer queue instantly signals an event.
a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100,1);
Thread.Sleep(500);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
hell
hell
hell
hell
and so on

The third parameter to the timer constructor is the duration in milliseconds after which
the timer should be fired. The second number can be either a 0 or any other number.
Zero means the timer fires once and any other number signifies it as a periodic timer.
This periodic timer keeps executing the function abc every 100 milliseconds, until told
to do otherwise.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100,1);
Thread.Sleep(115);
t.Change(100,0);
Thread.Sleep(1300);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
hell
hell
hell

The Change function is used to stop a periodic timer. Thus, the timer can be made to
run for some time in a periodic manner and thereafter, the Change can be used to stop
it from executing repeatedly.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100000,1);
Thread.Sleep(115);
t.Change(100,0);
Thread.Sleep(1300);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}

Output
hell

The timer initially is set to go off after a very long time. We let the main thread sleep for
a little while but then change the due time of the timer to a smaller value. Thus, we can
use the Change function to alter the initial settings of the timer.

a.cs
using System.Threading;
public class zzz
{
public static void Main()
{
zzz a = new zzz();
a.pqr();
}
public void pqr()
{
Timer t;
yyy b = new yyy();
t = new Timer(new TimerCallback(b.abc),this,100,300);
Thread.Sleep(1000);
}
}
public class yyy
{
public void abc(object o)
{
System.Console.WriteLine("hell");
}
}
Output
hell
hell
hell

Our dictum has always been to understand things one step at a time. The last
parameter to the timer constructor can either be zero or any other value. If the value is
100, it means that every hundred seconds the timer will go off, until it is stopped by a
Change or a Dispose. Earlier, we had used the number 1 to signify that the timer
should be called every 1 second. You are free to decide these timer values depending
upon what you intend to use the timer for. The values cannot be negative and cannot
exceed 4294967294 either. If you do so, an exception will occur. Exceptions, lest you
have forgotten, are generated at runtime only.

Thread Pool

a.cs
using System;
using System.Threading;
public class yyy {
public ManualResetEvent a = new ManualResetEvent(false);
public void abc(Object s)
{
a.WaitOne(10,true);
Console.WriteLine("{0} ", Thread.CurrentThread.GetHashCode());
}
};
public class zzz {
public static void Main() {
ManualResetEvent b = new ManualResetEvent(false);
yyy y = new yyy();
for (int i=1;i<=5;i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);
}
b.WaitOne(10000,true);
}
}
Output
2
2
2
2
2

The rationale of a thread pool is that the decision makers at Microsoft did not want us
to spend time taking care of threads like mothers take care of babies.

We will first explain the above program and then delve into the nitty-gritty of thread
pools. Since we are lazy, we use a for statement to avoid repeating the same code over
and over again. The function QueueUserWorkItem will be called at least five times
because it is placed in this for loop.

QueueUserWorkItem function requires one, or at the most, two parameters. The first
parameter, which is the name of a function, is most crucial. The name is never ever
assigned directly, but indirectly, using a WaitCallback object. The function to be called
is y.abc. Hence, it is passed a parameter to WaitCallback. The second parameter to the
function QueueUserWorkItem is the number 1. As a result, the function abc has to
accept a parameter of type object which will contain the value 1. A parameter of type
object does not have any useful code and by itself is totally useless.

Every entity in C# is finally derived from the object class. Thus, every entity is of an
object type. Whenever we are not aware of the parameter type to be received in a
function, or in cases where we want to receive multiple data types, the data type of
object is used. Object is like a rubber man; it can fit anywhere and everywhere. By
giving the function name abc to the function QueueUserWorkItem, abc will be called
five times by a thread. The function waits for a small amount of time at an event and
then uses the function GetHashCode to print the name/id of the thread. This function
will print the thread id that is calling abc.

From our output, we can conclude that only two threads execute the function abc.
Commenting the Wait statement will display the same id for the thread. This means
that no new thread executes the function. Thus, instead of two threads, only a single
thread is used. Efficiency, thy name is Thread Pool.

We are thus posting work items to a common place or thread pool. Then, a thread is
allocated to execute the work item or function. The first available thread from the
thread pool executes the function. We are only using one thread pool per program.

There is a lot of overhead in managing threads. A lot of time is spent figuring out which
thread is sleeping, i.e. waiting for an event to occur. Most of the time, threads are in
this inert state. At times, threads wake up to check/poll for a change that is expected to
occur, such as a key press, or to update some counter or status information and fall
asleep again. Threads are like bears, they love sleeping.

Thread Pooling is a means of managing threads more efficiently. This is done by


providing your application with a pool of threads that are managed by the system. All
threads that do work are called worker threads. There is a thread that monitors the
status of threads waiting at any of the wait functions. When the wait is over, the
callback function will be executed.

The next program demonstrates how a function can be called after a specified time
duration has elapsed. There is no way to cancel a function from being executed once it
has gone into the thread pool. A prayer will also not help. Timers and Waits use the
thread pool without our knowledge.

The thread pool is created when we call the function QueueUserWorkItem for the first
time or when we use a timer function. The number of threads that form part of a thread
pool is limited by the amount of memory available. By today's standards, this means
that more threads can be made available than we would ever need. These threads
receive the default stack size and run at the default priority. No special priority is
accorded. For the trivia fans, a thread can handle up to 63 wait operations. No concept
is perfect. Thus, one significant disadvantage of a thread pool is that it involves a long
calculation.

a.cs
using System;
using System.Threading;
public class yyy
{
public void abc(object s, bool b)
{
Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),b);
}
};
public class zzz
{
public static void Main()
{
ManualResetEvent a = new ManualResetEvent(false);
ManualResetEvent b = new ManualResetEvent(false);
yyy y = new yyy();
ThreadPool.RegisterWaitForSingleObject(a,new WaitOrTimerCallback(y.abc),1,1000,true);
b.WaitOne(10000,true);
}
}

Output
2 True

As mentioned earlier, we would like our function to be called after a certain wait. To
accomplish this, we use a function with a very large name called
RegisterWaitForSingleObject. The first parameter is the handle of an event. This has
been set to false or non signaled. The second parameter is a delegate of type
WaitOrTimerCallback which is similar to a WaitCallback. The difference is that the
signature of the function given to this function, which is abc, has to have two
parameters, viz. an object and a boolean variable. The third parameter is the object that
is to be passed to the function. The second last parameter is a timer that decides the
duration after which the function should be called. Finally, the last parameter decides
whether the function is to be executed only once or every time the timer expires.

As our event object is set to false, the function gets called after 1000 milliseconds.
When the timer expires, the function abc obtains a value of true.

By merely changing one line in the above example, the event can be changed to a
signaled state.

ManualResetEvent a = new ManualResetEvent(true);


Output
2 False

The function abc gets called immediately, unlike in the earlier program. Also, there is
no wait as the event is true or signaled. The function abc also knows why it has been
called since the bool parameter has a value of false, which was true in the earlier case.
Thus, the function abc can look at the value of its second parameter, which indicates
whether the timer has elapsed or the event has been signaled.

a.cs
using System;
using System.Threading;
public class yyy
{
public void Beta(object s, bool b)
{
Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),b);
}
};
public class zzz
{
public static void Main()
{
ManualResetEvent a = new ManualResetEvent(false);
ManualResetEvent b = new ManualResetEvent(false);
yyy y = new yyy();
ThreadPool.RegisterWaitForSingleObject(a,new WaitOrTimerCallback(y.Beta),1,10000,true);
a.Set();
b.WaitOne(10000,true);
}
}

Output
2 False

In this program, initially we set the event to false and then, using the Set function, we
set it to true immediately. Thus, the function gets called instantly. You would remember
that the function gets called whenever the event is in a signaled state or the timer
expires. This function first checks whether the wait object is signaled or not. If it is
signaled, it executes the function. If it is not, it waits for the timer to elapse or the wait
object to come into the signaled state, whichever occurs first.

Interlocked

a.cs
using System;
using System.Threading;
public class yyy
{
public ManualResetEvent a = new ManualResetEvent(false);
int i = 10;
public void abc(Object s)
{
Interlocked.Increment(ref i);
Console.WriteLine("{0} {1}", Thread.CurrentThread.GetHashCode(),i);
}
};
public class zzz
{
public static void Main() {
ManualResetEvent b = new ManualResetEvent(false);
yyy y = new yyy();
for (int i=1;i<=5;i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);
}
b.WaitOne(10000,true);
}
}

Output
2 11
2 12
2 13
2 14
2 15

Threads can be dangerous for the unwary. The function abc gets called five times by the
same or a different thread. Consider a case where one thread may be incrementing the
line i = i + 1. To do so, it will start by asking what is the value of i. Let us suppose that
it is 10. It then increments the value by 1 to make it 11. It may so happen that just as it
is about to read its value, its time slice may get over and another thread may increment
the variable i by 1 to make its value 12.

When the first thread receives its next time slice and checks the value of i, it will see the
value as 12 and not 11. Thus, the variable would have been incremented twice instead
of once.

The Interlocked class has a sole purpose in life, i.e. to ensure that the variables are not
incremented more than once. In a sense, it synchronizes access to a variable that is
being shared by a number of threads. The operation will either be done in a single time
slice or not done at all. It will not be carried over into another time slice ever. These
operations, which are guaranteed to be finished in a single time slice, are called atomic
operations.

a.cs
using System;
using System.Threading;
public class yyy
{
public ManualResetEvent a = new ManualResetEvent(false);
public int i = 10;
public void abc(Object s)
{
i++;
Interlocked.CompareExchange(ref i,100,12);
}
};
public class zzz
{
public static void Main()
{
ManualResetEvent b = new ManualResetEvent(false);
yyy y = new yyy();
for (int i=1;i<=5;i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(y.abc),1);
}
b.WaitOne(1000,true);
Console.WriteLine(y.i);
}
}

Output
103

The function abc gets called five times. If we intend to carry out comparisons, we would
face the above dilemma. Thus, we use a simple function called CompareExchange. This
functions checks the value of the first parameter that is passed as a reference, with the
third parameter, which in our case is 12. If it matches the value, the first parameter i.e.
i is assigned the value of the second parameter i.e. 100. All this is done in a single time
slice. Thus, whenever variable i becomes 12, its value shoots up to 100, and as a result,
the final answer becomes 103.

Like the Increment, there is a Decrement function also that reduces the value of a
variable by one. As an aside, a ref parameter cannot be replaced by an out parameter.
In the same vein, the Exchange function changes the value of two variables passed to it
as ref parameters. This is done in an atomic manner.

Potrebbero piacerti anche