Semaphores
A Semaphore inherits from System.Threading.WaitHandle
; as such, it has the WaitOne()
method. You are also able to use the static System.Threading.WaitHandle
, WaitAny()
, WaitAll()
, SignalAndWait()
methods for more complex tasks.
semaphores model like this. see below:
I read something that described a Semaphore being like a nightclub. It has a certain capacity, enforced by a bouncer. When full, no more people can enter the club, until one person leaves the club, at which point one more person may enter the club.
Let's see a simple example, where the Semaphore is set up to be able to handle two concurrent requests, and has an overall capacity of 5.
using System;
using System;
using System.Threading;
namespace SemaphoreTest
{
class Program
{
//initial count to be satified concurrently = 2
//maximum capacity = 5
static Semaphore sem = new Semaphore(2, 5);
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
new Thread(RunThread).Start("T" + i);
}
Console.ReadLine();
}
static void RunThread(object threadID)
{
while (true)
{
Console.WriteLine(string.Format(
"thread {0} is waiting on Semaphore",
threadID));
sem.WaitOne();
try
{
Console.WriteLine(string.Format(
"thread {0} is in the Semaphore, and is now Sleeping",
threadID));
Thread.Sleep(100);
Console.WriteLine(string.Format(
"thread {0} is releasing Semaphore",
threadID));
}
finally
{
//Allow another into the Semaphore
sem.Release();
}
}
}
}
}
Which results in something similar to this:
This example does show a working example of how a Semaphore
can be used to limit the number of concurrent threads, but it's not a very useful example. I will now outline some partially completed code, where we can use aSemaphore
to limit the number of threads trying to access a database with a limited number of connections available. The database can only accept a maximum of three concurrent connections. As I say, this code is incomplete, and does not work in its current state; it's for demonstration purposes only, and is not part of the attached demo app. using System;
using System.Threading;
using System.Data;
using System.Data.SqlClient;
namespace SemaphoreTest
{
/// <summary>
/// This example shows partially completed skeleton
/// code for consuming a limited resource, such as a
/// DB connection using a Semaphore
///
/// NOTE : THIS CODE WILL NOT RUN, ITS INCOMPLETE
/// DEMO ONLY CODE
/// </summary>
class RestrictedDBConnectionStringAccessUsingSemaphores
{
//initial count to be satified concurrently = 1
//maximum capacity = 3
static Semaphore sem = new Semaphore(1, 3);
static void Main(string[] args)
{
//start 5 new threads that all require a Database connection
//but as a DB connection is limited to 3, we use a Semaphore
//to ensure that the number of active connections will never
//exceed the total allowable DB connections
new Thread(RunCustomersThread).Start("ReadCustomersFromDB");
new Thread(RunOrdersThread).Start("ReadOrdersFromDB");
new Thread(RunProductsThread).Start("ReadProductsFromDB");
new Thread(RunSuppliersThread).Start("ReadSuppliersFromDB");
Console.ReadLine();
}
static void RunCustomersThread(object threadID)
{
//wait for the Semaphore
sem.WaitOne();
//the MAX DB connections must be within its limited
//so proceed to use the DB
using (new SqlConnection("<SOME_DB_CONNECT_STRING>"))
{
//do our business with the database
}
//Done with DB, so release Semaphore which will
//allow another into the Semaphore
sem.Release();
}
static void RunOrdersThread(object threadID)
{
//wait for the Semaphore
sem.WaitOne();
//the MAX DB connections must be within its limited
//so proceed to use the DB
using (new SqlConnection("<SOME_DB_CONNECT_STRING>"))
{
//do our business with the database
}
//Done with DB, so release Semaphore which will
//allow another into the Semaphore
sem.Release();
}
static void RunProductsThread(object threadID)
{
//wait for the Semaphore
sem.WaitOne();
//the MAX DB connections must be within its limited
//so proceed to use the DB
using (new SqlConnection("<SOME_DB_CONNECT_STRING>"))
{
//do our business with the database
}
//Done with DB, so release Semaphore which will
//allow another into the Semaphore
sem.Release();
}
static void RunSuppliersThread(object threadID)
{
//wait for the Semaphore
sem.WaitOne();
//the MAX DB connections must be within its limited
//so proceed to use the DB
using (new SqlConnection("<SOME_DB_CONNECT_STRING>"))
{
//do our business with the database
}
//Done with DB, so release Semaphore which will
//allow another into the Semaphore
sem.Release();
}
}
}
From this small example, it should be clear that the Semaphore
only allows a maximum of three threads (which was set in the Semaphore
constructor), so we can be sure that the database connections will also be kept within limit.