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
Tags: Learn C#, semaphores









Except where otherwise noted, content on this site is
December 13th, 2008 at 6:00 pm
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
December 13th, 2008 at 7:34 pm
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