If proper synchronization mechanism will be not applied then race condition will happen.
- Deadlock
- Starvation
- Priority Inversion
- Busy Waiting
The Following are some classic problems of Synchronization:
- The Producer-Consumer Problem
- The Readers-Writers Problem
- The Dining Philosopher Problem
These problems are used to test every newly proposed synchronization techniques.
Synchronization is handled with the following four categories:
The following are the four categories to handle Synchronization mechanism:
- Blocking Methods
- Locking Construct
- Signaling
- No Blocking Synchronization
Blocking Methods
In this technique, one thread wait for the another thread to finish for a particular period of time. Here, the execution is paused for some reason. The following are some Blocking methods in C#:
It pauses the execution of a program for a defined time. It does not utilize CPU during the pause time. It is useful for waiting on an external Task.
It is also a part of blocking mechanism in the thread synchronization. It is similar to Sleep but it does not pauses all threads. It pauses the calling thread until the thread whose join method is called has completed.
Example:
- class Program
- {
- static void Main(string[] args)
- {
- Thread thread1 = new Thread(Method1);
- thread1.Start();
-
- Thread thread2 = new Thread(Method2);
- thread2.Start();
-
- thread1.Join();
- Console.WriteLine("After Thread1");
-
- thread2.Join();
- Console.WriteLine("After Thread2");
- Console.ReadKey();
-
- }
-
- private static void Method2(object obj)
- {
- Console.WriteLine("Thread1 Executed.");
- }
-
- private static void Method1(object obj)
- {
- Console.WriteLine("Thread2 Executed");
- }
-
-
- }
Output:
Thread2 Executed.
Thread1 Executed.
After Thread1
After Thread2
Task.Wait
It is a blocking Synchronization method which allows the calling thread to wait until the current task has been completed.
Example:
- class Program
- {
- static void Main(string[] args)
- {
- Task task = Task.Run(() =>
- {
- Random randomNumbers = new Random();
- long sum = 0;
- int count = 1000000;
- for (int i = 1; i <= count; i++)
- {
- int randomNumber = randomNumbers.Next(0, 101);
- sum += randomNumber;
- }
-
- Console.WriteLine("Total:{0}", sum);
- Console.WriteLine("Count: {0}", count);
- });
- task.Wait();
-
- Console.ReadKey();
- }
- }
Output:
Total: 50028434
Count: 1000000
Locking
Locking is also a synchronization mechanism. It limits the access to a resource in multiple thread. Exclusive locking mechanism is used for it.
The following are the two main Locking mechanism:
It locks the critical section of code so that only one thread can execute the critical section of code at a time. If another thread tries to enter into critical section of code then it is prevented and blocked and then it will wait until the object is released from the using thread.
Example:
- class Program
- {
- decimal totalBalance =50000;
- private Object myLock = new Object();
-
- static void Main(string[] args)
- {
- Program program = new Program();
- program.WithDraw(5000);
- Console.ReadKey();
- }
-
- public void WithDraw(decimal amount)
- {
- lock (myLock)
- {
- if (amount > totalBalance)
- {
- Console.WriteLine("Insufficient Amount.");
- }
-
- totalBalance -= amount;
- Console.WriteLine("Total Balance {0}",totalBalance);
- }
- }
-
- }
Output:
Total Balance 45000
Note: Avoid lock on a public type, or instances beyond the control of code.
Mutex
Mutex stands for Mutual Exclusion. The Mutex type ensures blocks of code are executed only once at a time. It is basically used in a situation where resources has to be shared by multiple threads simultaneously.