Semaphore - Thread Synchronization

Introduction

Thread Synchronization ensures that two or more concurrent processes or threads do not simultaneously execute some particular program segments. It makes one thread wait, until it finishes other thread, when one thread already executes in the critical section. If proper synchronization techniques are not applied, it may cause race condition.
 
There are different ways to do thread synchronization. Locking is the famous way to to do thread synchronization. Basically, there are two kinds of locks - Exclusive and Non-Exclusive Lock.
 
A Non-Exclusive lock is a lock that notifies others that you are considering  changing the file. When you have a file locked non-exclusively, others can modify the file. Thus, Semaphore comes under the category of Non-Exclusive Lock. 
 
A Semaphore restricts the number of simultaneous users of the shared resources up to a maximum number. It ensures not more than a specific number of concurrent threads, which can access a resource.
 
A Semaphore is like a nightclub, it has a certain capacity, enforced by bouncer. Once it is full, no more people can enter and people wait outside in the form of queue. For each person that leaves, one people enters from the head of the queue into nightclub.
 
There are two functionalities- Semaphore and SemaphoreSlim. Semaphore incurs about 1 microseconds calling WaitOne or Release while SemaphoreSlim incurs about a quarter of it.
 
Example

This is a basic example of Semaphore, where 5 threads are wanted to enter and execute but, I will restrict it up to 3 threads, using Semaphore. 
  1. using System;  
  2. using System.Text;  
  3. using System.Threading.Tasks;  
  4. using System.Threading;  
  5.   
  6. namespace Semaphore  
  7. {  
  8.     public class TheSempahore  
  9.     {  
  10.         static SemaphoreSlim sema = new SemaphoreSlim(3);  
  11.   
  12.         public static void StartExecute()  
  13.         {  
  14.             for (int i = 1; i <= 5; i++)  
  15.             {  
  16.                 new Thread(Enter).Start(i);  
  17.             }  
  18.         }  
  19.   
  20.         private static void Enter(object id)  
  21.         {  
  22.             Console.WriteLine("{0} wants to enter", id);  
  23.             sema.Wait();  
  24.   
  25.             Console.WriteLine("Id {0} is in!", id);  
  26.   
  27.             Thread.Sleep(1000 * (int)id);  
  28.   
  29.             Console.WriteLine("{0} is leaving", id);  
  30.   
  31.             sema.Release();  
  32.         }  
  33.     }  
  34. }  
  35.   
  36.   
  37. Now, Call the method into entry point:  
  38.   
  39. class program  
  40. {  
  41.   
  42.    public static void main()  
  43.      {  
  44.          TheSempahore.StartExecute();  
  45.          Console.readkey();  
  46.       }  
  47. }  
Output
 
1 wants to enter
1 is in!
2 wants to enter
2 is in!
3 wants to enter
3 is in!
4 wants to enter
5 wants to enter
1 is leaving
4 is in!
2 is leaving
5 is in!

Example - Print Odd and Even numbers, using Thread.
  1. public class OddEven  
  2.     {  
  3.         static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);  
  4.         static Thread Odd;  
  5.         static Thread Even;  
  6.         static LinkedList<int> Numbers = new LinkedList<int>();  
  7.   
  8.         private static void PrintOdd()  
  9.         {  
  10.             while (Numbers.Count > 0)  
  11.             {  
  12.                 semaphoreSlim.Wait();  
  13.                 var a = Numbers.FirstOrDefault();  
  14.                 if (a % 2 != 0)  
  15.                 {  
  16.                     Console.WriteLine(Thread.CurrentThread.Name + " " + a);  
  17.                     Numbers.RemoveFirst();  
  18.                 }  
  19.                 semaphoreSlim.Release();  
  20.             }  
  21.         }  
  22.         private static void PrintEven()  
  23.         {  
  24.             while (Numbers.Count > 1)  
  25.             {  
  26.                 semaphoreSlim.Wait();  
  27.                 var a = Numbers.FirstOrDefault();  
  28.                 if (a % 2 == 0)  
  29.                 {  
  30.                     Console.WriteLine(Thread.CurrentThread.Name + " " + a);  
  31.                     Numbers.RemoveFirst();  
  32.                 }  
  33.                 semaphoreSlim.Release();  
  34.             }  
  35.         }  
  36.   
  37.         public static void PrintOddEven()  
  38.         {  
  39.             for (int i = 1; i <= 10; i++)  
  40.             {  
  41.                 Numbers.AddLast(i);  
  42.             }  
  43.   
  44.             Odd = new Thread(PrintOdd);  
  45.             Even = new Thread(PrintEven);  
  46.   
  47.             Odd.Name = "Odd : ";  
  48.             Even.Name = "Even :";  
  49.             Odd.Start();  
  50.             Even.Start();  
  51.             Odd.Join();  
  52.             Even.Join();  
  53.         }  
  54.     }  
Now, execute the method, shown above.
  1. public class program  
  2. {  
  3.    OddEven.PrintOddEven();  
  4.    Console.ReadKey();  
  5. }  
Output
 
Odd : 1
Even : 2
Odd : 3
Even : 4
Odd : 5
Even: 6
Odd : 7
Even : 8
Odd : 9
Even : 10

Conclusion

Thus, Semaphore is a type of Non-Exclusive Synchronization lock. Semaphore is the number of free identical toilet keys.
 
Example, say we have four toilets with identical locks and keys. The semaphore count - the count of keys - is set to 4 at the beginning (all four toilets are free), then the count value is decremented as people are coming in. If all toilets are full, i.e. there are no free keys left, the semaphore count is 0. Now, when one person leaves the toilet, semaphore is increased to 1 (one free key) and is given to the next person in the queue.
Ebook Download
View all
Learn
View all