The Problem “Concurrency”
When you build multithreaded application, your program needs to ensure that shared data should be protected from against the possibility of multiple threads engagement with its value. What’s gonna happen if multiple threads were accessing the data at the same point? CLR can suspend your any thread for a while who’s going to update the value or is in the middle of updating the value and same time a thread comes to read that value which is not completely updated, that thread is reading an uncompleted/unstable data.
To illustrate the problem of concurrency let write some line of code
class Program
{
static void Main(string[] args)
{
Console.WriteLine("----Synchronnization of Threads-----");
Console.WriteLine("Main Thread {0}", Thread.CurrentThread.ManagedThreadId);
Printer p = new Printer();
Thread[] threads = new Thread[5];
//Queue 5 threads
for (int i = 0; i < 5; i++)
{
threads[i] = new Thread(new ThreadStart(p.PrintNumbersNonSync));
}
foreach (Thread t in threads)
{
t.Start();
}
Console.ReadLine();
}
}
class Printer
{
public void PrintNumbersNonSync()
{
Console.WriteLine(" ");
Console.WriteLine("Executing Thread {0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
}
Run this program multiple times and watch the output:
Output1:
Output2:
your output could be different from these.
As you can see all the output would be different as many time you run the program.
What happening here is that all the threads are sharing the same object of Printer class and trying to execute the same function at the same time so every time the shared data is being updated in a random pattern which is an unstable state.
Synchronization of threads Solution to the problem of concurrency
Use the locks whenever there’s a Shared section. C# provides Lock keyword that can be used to lock the section that is being accessed by multiple threads. This is the very basic technique to avoid the instability in multithreaded environment while programming with c#.
The Lock keyword requires you to specify the token (an object reference) that must be acquired by a thread to enter within the lock scope. In case of private method you can lock it down by passing the reference of current type using “this” keyword.
For e.g.
public void PrintNumbersSynchronized()
{
//Synchronization thread
lock (this)
{
Console.WriteLine(" ");
Console.WriteLine("Executing Thread {0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
}
However if you are locking down a region of code within a public member, it is safer (and best practice) to declare a private object member variable to server as the lock token:
class Printer
{
// Synchronization token
private Object ThreadLock = new object();
public void PrintNumbersSynchronized()
{
//Synchronization thread
lock (ThreadLock)
{
Console.WriteLine(" ");
Console.WriteLine("Executing Thread {0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
}
}
Output:
Other methods to perform the synchronization
Monitor is class in System.Threading namespace that also provides the same functionality. Actually lock is just a shorthand notation of Monitor class. Once compiler processed a lock it actually resolves to the following:
public void PrintNumbersSync()
{
Monitor.Enter(ThreadLock);
try
{
Console.WriteLine(" ");
Console.WriteLine("Executing Thread {0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
finally
{
Monitor.Exit(ThreadLock);
}
}
Here little more code you can see having the try..finally block. Other than Enter() and Exit() methods, Monitor class also provides the methods like Monitor.Pulse() and PulseAll() to inform the waiting threads that its completed.
Simple operations using the Interlocked Type
To perform some basic operation it’s not necessary that you write the block using the lock or Monitor. System.Threading namespace provides an interesting class that can help you out.
Now you can replace this
lock (ThreadLock)
{
intVal++;
}
By
int newVal = Interlocked.Increment(ref intVal);
It returns the new values of updated variable as well as update the referenced value at the same time.
Similarly,
Add()
Exchange() //for swapping
CompareExchange() // Compare a value and then exchange
Equals()
Decrement()
Read()
Are some basic operations you can perform in a multithreaded environment to make your threadsafe using the Interlocked class.
Synchronization using the [Synchronization] attribute
The Synchronization attribute is a member of System.Runtime.Remoting.Contexts namespace. In essence, this class-level attribute effectively lock downs all instance members code of the object for the thread safety. Additionally you have to derive your class from ContextBoundObject to keep your object with in the contextual boundries.
Here’s the complete code:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("----Synchronnization of Threads-----");
Console.WriteLine("Main Thread {0}", Thread.CurrentThread.ManagedThreadId);
Printer p = new Printer();
Thread[] threads = new Thread[5];
//Queue 5 threads
for (int i = 0; i < 5; i++)
{
threads[i] = new Thread(new ThreadStart(p.PrintNumbersNonSync));
}
foreach (Thread t in threads)
{
t.Start();
}
Console.ReadLine();
}
}
[Synchronization]
class Printer : ContextBoundObject
{
public void PrintNumbersNonSync()
{
Console.WriteLine(" ");
Console.WriteLine("Executing Thread {0}", Thread.CurrentThread.ManagedThreadId);
for (int i = 1; i <= 10; i++)
{
Console.Write(i + " ");
}
}
}
Output:
This appraoch is a lazy way to write thread safe code because CLR can lock non-thread sensitive data and that could be an victim of OverLocking. So please choose this approach wisely and carefully.
Happy Threading…!!