Threading with Semaphore in C#


Introduction

The semaphore class works similar to the Monitor and Mutex class but lets you set a limit on how many threads have access to a critical section. It's often described as a nightclub (the semaphore) where the visitors (threads) stands in a queue outside the nightclub waiting for someone to leave in order to gain entrance.

A critical section is a piece of code that accesses a shared resource (data structure or device) but the condition is that only one thread can enter in this section in a time.

Introducing : System.Threading.Semaphore

System.Threading.Semaphore provide all the method and property which are require to implement Semaphore. To use a semaphore in C#, you first need to instantiate an instance of a Semaphore object. The constructor, at a minimum, takes two parameters. The first is the number of resource slots initially available when the object is instantiated. The second parameter is the maximum number of slots available.  If you want to reserve some slots for the calling thread, you can do so by making the first parameter smaller than the second. To reserve all slots for new threads, you should make both parameters the same.

After you instantiated your Semaphore object, you simply need to call the WaitOne() method when entering an area of code that you want restricted to a certain number of threads. When processing finishes, call the Release() method to release the slot back to the pool.

The count on a semaphore is decremented each time a thread enters the semaphore, and incremented when a thread releases the semaphore. When the count is zero, subsequent requests block until other threads release the semaphore. When all threads have released the semaphore, the count is at the maximum value specified when the semaphore was created.

Creating a new semaphore is accomplished through one of the existing constructors:

  • Semaphore(Int32,Int32) : Initializes a new instance of the Semaphore class, specifying the maximum number of concurrent entries and optionally reserving some entries.
  • Semaphore(Int32,Int32,String) : Initializes a new instance of the Semaphore class, specifying the maximum number of concurrent entries, optionally reserving some entries for the calling thread, and optionally specifying the name of a system semaphore object.
  • Semaphore(Int32,Int32,String,Boolean) : Initializes a new instance of the Semaphore class, specifying the maximum number of concurrent entries, optionally reserving some entries for the calling thread, optionally specifying the name of a system semaphore object, and specifying a variable that receives a value indicating whether a new system semaphore was created.
  • Semaphore(Int32,Int32,String,Boolean,SemaphoreSecurity) : Initializes a new instance of the Semaphore class, specifying the maximum number of concurrent entries, optionally reserving some entries for the calling thread, optionally specifying the name of a system semaphore object, specifying a variable that receives a value indicating whether a new system semaphore was created, and specifying security access control for the system semaphore.
Using the code

using System;
using System.Threading;
namespace threadingSemaphore
{
class Akshay
    {
        static Thread[] threads = new Thread[10];
        static Semaphore sem = new Semaphore(3, 3);
        static void C_sharpcorner()
        {
            Console.WriteLine("{0} is waiting in line...", Thread.CurrentThread.Name);
            sem.WaitOne();
            Console.WriteLine("{0} enters the C_sharpcorner.com!", Thread.CurrentThread.Name);
            Thread.Sleep(300);
            Console.WriteLine("{0} is leaving the C_sharpcorner.com", Thread.CurrentThread.Name);
            sem.Release();
        }
       static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                threads[i] = new Thread(C_sharpcorner);
                threads[i].Name = "thread_" + i;
                threads[i].Start();
            }
           Console.Read();
         }
    }
}

Output

1.gif

Some more important facts about Semaphore

  • There is no guaranteed order, such as FIFO or LIFO, in which blocked threads enter the semaphore.
  • The Semaphore class does not enforce thread identity on calls to WaitOne() or Release(). It is the programmer's responsibility to ensure that threads do not release the semaphore too many times.
  • For example, suppose a semaphore has a maximum count of two, and that thread A and thread B both enter the semaphore. If a programming error in thread B causes it to call Release twice, both calls succeed. The count on the semaphore is full, and when thread A eventually calls Release, a SemaphoreFullException is thrown.
  • Semaphores are of two types: local semaphores and named system semaphores.
  • If you create a Semaphore object using a constructor that accepts a name, it is associated with an operating-system semaphore of that name.
  • Named system semaphores are visible throughout the operating system, and can be used to synchronize the activities of processes.
  • You can create multiple Semaphore objects that represent the same named system semaphore, and you can use the OpenExisting() method to open an existing named system semaphore.
  • A local semaphore exists only within your process. It can be used by any thread in your process that has a reference to the local Semaphore object.
  • Each Semaphore object is a separate local semaphore.

Up Next
    Ebook Download
    View all
    Learn
    View all