Thursday, 30 January 2014

Controlling concurrencies with semaphores.

Semaphore is one of the primitive objects together with mutex that inherits from the WaitHandle class existing in the operating system level. It is used for controlling and synchronizing thread access to shared state and resources. Like mutex, it can be used to control the inter-process access to shared resource like disk IO and to control the number of threads accessing a resource at any given point in time.

Semaphores come in two different flavours : Semaphore and SemaphoreSlim. SemaphoreSlim was added to .net framework version 4.0. Its purpose is the same with the more primitive Semaphore but it reduces the latency that is usually incurred when using the Semaphore class.

The major function of the Semaphore object is to control the total number of thread that is accessing a shared resource at a particular point in time. This is the only difference between it and other Synchronization objects like Mutex which allow only a single thread at a time to access a shared resource.

Semaphores can be created either as a local champion or as an international champion. What this means is that you can create semaphore as a local object that is only accessible within the context in which it is created or as an inter-process object that can be accessed from different processes in the system.

To create a local Semaphore object just call the constructor that just accepts two int arguments. This two arguments just indicate the total number of threads that can access a shared resource at a time and the number of vacant spaces that is waiting to be filled. for example

                                            var locker =  new Semaphore(5, 2); 

The new local semaphore object that we created above indicates that at any point in time, only five threads will be allowed to be execute the shared resource and that at the moment, there are two vacant posts waiting to be filled.

Creating an inter-process Semaphore is also very simple it just requires you to pass a third argument to the constructor object. This argument is just a string that serves as the name of the semaphore. It can be used to access the object from another process. However, always check if there is any semaphore object that has been created by the same name by calling TryOpenExisting(semaphoreName) static method. This will ensure that at any point in time only one Semaphore object exists and also help prevent unnecessary exceptions.

Another thing to take note of about Semaphors is that they Thread agnostic. What this means is that unlike other locking objects that is usually released by the thread that block it in the first place, any thread  that is currently executing the shared resource can call Release() a certain number of times thereby releasing not just itself  but others from the shared state. Note that if Release() is called more number of times than the current Semaphores' capacity, a SemaphoreFullException will be raised.

If a thread wants to access a shared state all it has to do is call WaitOne() instance method. If there is still a vacant position waiting to be filled then access will be granted. If not, then the thread will be blocked until vacancy opens up in the shard state. For example:

class SpecialExecutor
{
  static Semaphore _sem = new Semaphore (3, 3);    
 
  static void Main()
  {
    for (int i = 1; i <= 5; i++) new Thread (Execute).Start (i);
  }
 
  static void Execute (object id)
  {
    Console.WriteLine (id + " is about to begin execution");
    _sem.WaitOne();
    Console.WriteLine (id + " is  now executing...");           
    Thread.Sleep (1000 * (int) id);        //Am only sleeping but they have no                                            //idea    
    Console.WriteLine(id + " am done executing"); //ok, am now feeling great     
    _sem.Release();
  }
}


This will print
1 is about to begin execution
1 is now executing...
2 is about to begin execution
2 is now executing....
3 is about to begin execution
3  is now executing...
4  is about to begin execution
5 is about to begin execution
1 am done executing
4 is now executing...
2 am done executing
5 is now executing...



The code above pretty much explains most of the code we have discussing.

Summary

Semaphore serves as a great way to allow a specific number of threads to access a shared resources rather than restricting access to a single thread at a time which might affect the performance of your app.

Further Resources

  1. Threading in C#
  2. Semaphore Class
  3. Multithread in C# (note: orignally written in German but you can translate using google translator)
Happy coding











No comments:

Post a Comment