Home About

November 21st, 2008

Using Semaphores in C# - 2

Semaphores allow us to synchronize threads inside a C# application. Semaphores are not used to transfer large amounts of data as are pipe’s or queues, instead they are intended to let multiple threads synchronize their actions. Typically you will have some kind of resource that only one , or a limited number of threads are allowed to access simultaneously.

A semaphore works by keeping a counter. Each time a thread obtains the semaphore the counter is reduced and each time the thread returns the semaphore it is increased. It is possible for a thread to wait for a semaphore to become available, thus stopping their execution until what they intend to do becomes possible.

A pseudo code implementation of obtaining a Semaphore would be like the following:

while (true)
{
    if (semaphore_value > 0) { semaphore_value--; break }
}

The problem is of course that this isn’t an atomic operation. If another thread is trying to obtain the semaphore at the same time they might both discover that semaphore_value>0 and both decrease semaphore_value.

We can avoid this from happening by protecting the code through the C# lock() statement:

while (true)
{
    lock(this)
    {
        if (semaphore_value > 0) { semaphore_value--; break }
    }
}
// As soon a we break out of the while, the lock is also broken

Of course the above code is still horribly inefficient, we need to continuously test if the variable has become available. It is better if the thread goes to sleep and wakes up when the semaphore has been made available. A possible implementation of this is by using a ManualResetEvent (as implemented in System.Threading). A ManualResetEvent puts the current thread to sleep when it calls WaitOne() until another thread calls Set() on the ManualResetEvent.

while (true)
{
    lock(this)
    {
        if (semaphore_value > 0) { semaphore_value--; break }
    }
    // Wait for a process to return a semaphore
    semaphore_manual_reset_event.WaitOne();
}

On returning our pseudo-code semaphore, another thread would then call semaphore_manual_reset_event.Set() to release all waiting threads. They would all rush over and see who could first claim the semaphore.

Introducing System.Threading.Semaphore

Thankfully all of this is actually implemented in the (System.Threading) Semaphore class. A Semaphore is created with two parameters, the initial number of available slots and the maximum number of slots (eg. it is possible to release the semaphore up to the maximum number of times, a further increase will throw a “SemaphoreFullException”).

Semaphores in C# are not First in First Out, the implementation actually does not guarantee the order in which tasks are released.

Semaphore myTCPSemaphore = new Semaphore( 1, 1 );   // Initially there is 1 slot available, and the maximum is also 1

myTCPSemaphore.WaitOne();  // Only one thread can complete the following task simultaniously

// Prepare and send a TCP-IP message
myTCPSemaphore.Release(); //  We are finished, allow another task (if waiting) to enter

Note that the above is similar to how lock() works, it also only allows 1 task to enter. However a lock() statement is limited to a single segment of code. Once obtained a semaphore is held until the thread returns it to the operating system. Another strategy is to create a semaphore with an intial count of 0. In this way the creating thread implicitly owns the semaphore. It needs to make a call to “Release()” to make the Semaphore available to other threads.

Example Code

The following example shows how a program can allocate 2 resource slots and use them to protect a critical section. Each worker thread needs to get (WaitOne) access, and must release it as soon as it has finished with it.

using System;
using System.Threading;

public class SemaphoreExample

{
    // A semaphore that simulates a limited resource pool.
    private static Semaphore ResourceLock;

    public static void Main()
    {
        // Create a semaphore that can satisfy up to two
        // concurrent requests. Use an initial count of zero,
        // so that the entire semaphore count is initially
        // owned by the main program thread.
        //
        ResourceLock = new Semaphore(0, 2);

        // Create and start threads and assign them a name
        for (int Lp = 1; Lp <= 5; Lp++)
        {
            Thread workThread = new Thread(new ThreadStart(Worker));

            // Start the thread, but name it first
            workThread.Name = Lp.ToString();
            workThread.Start();
        }

        // By releasing 2 here we make 2 slots available for other tasks to start processing
        Console.WriteLine("Main Releases 2.");
        ResourceLock.Release(2);

        Console.WriteLine("Main exits.");
    }

    private static void NonCriticalSection(string Id)
    {
        Console.WriteLine("Thread {0} enters the non-critical section", Id);
    }

    private static void CriticalSection(string Id)
    {
        Console.WriteLine("Thread {0} enters the critical section", Id);
    }

    private static void Worker()
    {
        // Obtain the thread name
        string Name = Thread.CurrentThread.Name;
        Console.WriteLine("Thread {0} has started", Name);

        for (int Lp = 0; Lp < 2; Lp++)
        {
            // This funtion is not critical -- anyone can enter
            NonCriticalSection(Name);

            // We need to aquire access to be able to enter the critical section
            ResourceLock.WaitOne();

            CriticalSection(Name);

            // We need to think about this for a bit -- some algortihm
            Thread.Sleep(500);

            // We have finished our job, so release the semaphore
            int prevCount = ResourceLock.Release();

            // Report on how many places are available
            Console.WriteLine("Thread {0} released the semaphore, previously there were {1} slots open.",
                Name,   // {0}
                prevCount); // {1}
        }

        Console.WriteLine("Thread {0} has finished", Name);

    }

}

The above code is tested and runs under both Microsoft Visual C# and Mono. Sample output:

Thread 1 has started
Thread 3 has started
Thread 3 enters the non-critical section
Thread 2 has started
Thread 2 enters the non-critical section
Thread 4 has started
Thread 4 enters the non-critical section
Thread 1 enters the non-critical section
Main Releases 2.
Thread 5 has started
Thread 5 enters the non-critical section
Thread 2 enters the critical section
Thread 4 enters the critical section
Main exits.
Thread 2 released the semaphore, previously there were 0 slots open.
Thread 2 enters the non-critical section
Thread 2 enters the critical section
Thread 4 released the semaphore, previously there were 0 slots open.
Thread 4 enters the non-critical section
Thread 4 enters the critical section
Thread 2 released the semaphore, previously there were 0 slots open.
Thread 2 has finished
Thread 3 enters the critical section
Thread 4 released the semaphore, previously there were 0 slots open.
Thread 4 has finished
Thread 1 enters the critical section
Thread 3 released the semaphore, previously there were 0 slots open.
Thread 3 enters the non-critical section
Thread 3 enters the critical section
Thread 1 released the semaphore, previously there were 0 slots open.
Thread 1 enters the non-critical section
Thread 1 enters the critical section
Thread 3 released the semaphore, previously there were 0 slots open.
Thread 3 has finished
Thread 5 enters the critical section
Thread 1 released the semaphore, previously there were 0 slots open.
Thread 1 has finished
Thread 5 released the semaphore, previously there were 1 slots open.
Thread 5 enters the non-critical section
Thread 5 enters the critical section
Thread 5 released the semaphore, previously there were 1 slots open.
Thread 5 has finished

Image credit: My Buffo

Share and Enjoy:
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google
  • Reddit

Tags: ,

2 Responses to “Using Semaphores in C#”

  1. Fatih YILDIRIM Says:

    Thanks for your article but how about managing processes on a shared resource?This articles describes managing threads in same process.I need managing processes on a shared resource.Thnks

  2. Martijn Says:

    Hi Fatih,

    What you are looking for is a named system semaphore. C# can create or wait for Win32 system semaphores, so the other process can be written in any language or also in C#. You can find a description of how this works on MSDN:

    http://msdn.microsoft.com/en-us/library/kbk057cx.aspx

    If I have a moment I will write a small example.

    Cheers,
    Martijn

Leave a Reply


Recent Comments
  • Ales: Hi, Thanks for the code… I must say I did not experience any errors decrypting any of my messages. I even...
  • JC: Thanks very useful and well explained
  • Thomas: This is a public static class written in the C# language that does not save state. You can call into the...
  • Simon: Thank you very much for this post! It helped me essentially to overcome obstacels to work with mono!
  • Graham: This is a good research for keyboard shortcuts! Some shortcuts are also compatible for Windows OS. I have...