You might
be working in a multithreaded application wherein more than one thread
can access your code at the same time. There are certain critical situations
wherein the code has to be accessed by only thread at a time.
In such cases, you can use lock statement to lock such block of code.
The lock can be obtained by only one thread at a time. If another thread
is trying to access the locked code, then it has to wait until the earlier
thread finishes its job and then it can acquire the lock and execute the
code.
Lock can
be acquired using the following syntax:
lock(expression)
{
//Code to be locked
}
In this syntax,
expression can accept any reference type. There are certain restrictions
in the expression based on which locking has to happen:
Expression
cannot be a value type
Even though the expression can accept any reference type, ensure
that you do not use the following as expression since it might lead to
serious threading issues:
o this
o typeOf(<specific type>)
o <string>
Though these values are legal, they are not recommended.
It is recommended that the reference type used as expression doesnt
have its access modifier as public
The syntax
mentioned above is equivalent to:
Object object1
= <expression>;
Monitor.Enter(object1);
try {
//code to be locked
}
finally {
Monitor.Exit(object1);
}
Note that
Monitor class belongs to System.Threading namespace.
For better
understanding of why locking is required, consider the following code
scenario:
class sampleClass
{
private int member1;
sampleClass(int data1) {
member1 = data1;
}
public void criticalComputation(int data) {
if(member1 < 0) {
throw new Exception(Member1 cannot be negative);
}
if(member1 > data) {
member1 -= data;
}
else {
Console.WriteLine(Insufficient Value in member1);
}
}
}
In this example,
there will be no problem as long as your application is single threaded
and only one thread is accessing the criticalComputation method at a time.
Assume that the member1 value is initiated to 100. You call the criticalComputation
method with the parameter 90. Control enters your code block. Since 100>
0 , it moves into computation. Now member1 value will be set to 100-90
which is 10. The next call to the method will execute with the member1
value as 10.
But this
doesnt happen in the same way in multithreaded application. Assume
that your member1 is initialized to 100. Now two threads are accessing
your code at the same time. First thread calls the criticalComputation
method with the value 90 and second thread with the value 50.
Both the
threads are accessing your code at the same time and both have checked
the condition member1>0. Since member1=100, both the threads satisfy
the conditions member1>0 and member1>data and they continue with
the computation. When thread1 is performing the computation, member1 will
be 100-90 = 10. Thread2 does the computation and member1 will be 10-50
= -40.
Is this the
expected result? NO. This will throw the user defined exception coded
above. How can you avoid this situation? You can easily avoid it using
lock statement. The above code is modified to use lock statement as shown
below:
class sampleClass
{
private int member1;
private Object lockObj = new Object();
sampleClass(int data1) {
member1 = data1;
}
public void criticalComputation(int data) {
if(member1 < 0) {
throw new Exception(Member1 cannot be negative);
}
lock(lockObj) {
if(member1 >= data) {
member1 -= data;
}
else {
Console.WriteLine(Insufficient Value in member1);
}
}
}
}
class testClass {
sampleClass obj = new sampleClass(100);
Thread[] threadInstances = new Thread[20];
for (int index = 0; index < 20; index++) {
Thread sample = new Thread(obj.criticalComputation);
threadInstances[index] = sample;
}
for (int index = 0; index < 20; index++)
{
threadInstances[index].Start();
}
}
In this modified
code, you have locked the computation code in criticalComputation method.
In Main method of testClass, you initialize and start 20 threads in one
go. So there are enough chances for multiple threads to access code of
criticalComputation method in parallel way. But still, to your surprise
the exception stating Member1 cannot be negative will never
be thrown since the synchronization of threads is achieved using lock
statement.