Recipe18.10.Granting Multiple Access to Resources with a Semaphore


Recipe 18.10. Granting Multiple Access to Resources with a Semaphore

Problem

You have a resource you want only a certain number of clients to access at a given time.

Solution

Use a semaphore to enable resource-counted access to the resource. For example, if you have an Xbox and a copy of Halo2 (the resource) and a development staff eager to blow off some steam (the clients), you have to synchronize access to the Xbox. Since the Xbox has four controllers, up to four clients can be playing at any given time. The rules of the house are that when you die, you give up your controller.

To accomplish this, create a class called Halo2Session with a Semaphore called _Xbox like this:

 public class Halo2Session {     // A semaphore that simulates a limited resource pool     //     private static Semaphore _Xbox;     // Player handles for Xbox     private static string [] _handles = new string [9]{"Igor",                                 "AxeMan",                                 "Frosty",                                 "Dr. Death",                                 "HaPpyCaMpEr",                                 "Executioner",                                 "FragMan",                                 "Beatdown",                                 "Stoney"                                 }; 

The _handles array is an array of player names that will be used. In order to get things rolling, you need to call the Play method, shown in Example 18-7, on the Halo2Session class.

Example 18-7. Play method

 public static void Play() {     // An Xbox has four controller ports so four people can play at a time.     // We use 4 as the max and zero to start with as we want players     // to queue up at first until the Xbox boots and loads the game.     //     _Xbox = new Semaphore(0, 4, "Xbox");     ManualResetEvent GameOver = new ManualResetEvent(false);     //     // Nine players log in to play.     //     for(int i = 0; i < 9; i++)     {         Thread t = new Thread(new ParameterizedThreadStart(XboxPlayer.JoinIn));         XboxPlayer.Data playerData = new XboxPlayer.Data();         // Set the handle.         playerData._handle = _handles[i];         // Set the game over event.         playerData._GameOver = GameOver;        // Put a name on the thread.        t.Name = _handles[i];        // Fire up the player with the data.        t.Start(playerData);     }     // Wait for the Xbox to spin up and load Halo2 (3 seconds).     Console.WriteLine("Xbox initializing…");     Thread.Sleep(3000);     Console.WriteLine("Halo2 loaded & ready, allowing 4 players in now…");     // The Xbox has the whole semaphore count. We call     // Release(4) to open up four slots and     // allow the waiting players to enter the Xbox(semaphore)     // up to four at a time.     //     _Xbox.Release(4);     // Wait for the game to end…     GameOver.WaitOne(); } 

The first thing the Play method does is to create a new semaphore that has a maximum resource count of 4 and a name of Xbox. This is the semaphore that will be used by all of the player threads to gain access to the game. A ManualResetEvent called GameOver is created to track when the game has ended.

 public class XboxPlayer {     public class Data     {         public ManualResetEvent _GameOver;         public string _handle;     }     //… more class } 

To simulate the nine developers, you create nine threads, each with its own XboxPlayer.Data class instance to contain the player name and a reference to the GameOver ManualResetEvent. The thread creation is using the new (in the .NET Framework Version 2.0) ParameterizedThreadStart delegate, which takes the method to execute on the new thread in the constructor, but also allows you to pass the data object directly to a new overload of the THRead.Start method.

Once the players are in motion, the Xbox "initializes" and then calls Release on the semaphore to open four slots for player threads to grab onto, then waits until it detects that the game is over from the firing of the GameOver event.

The players initialize on separate threads and run the JoinIn method, shown in Example 18-8. First they open the Xbox semaphore by name and get the data that was passed to the thread. Once they have the semaphore, they call WaitOne to queue up to play. Once the initial four slots are opened or another player "dies," then the call to WaitOne unblocks and the player "plays" for a random amount of time, then dies. Once the players are dead, they call Release on the semaphore to indicate their slot is now open. If the semaphore reaches its maximum resource count, the GameOver event is set.

Example 18-8. JoinIn method

 public static void JoinIn(object info) {     // Open up the semaphore by name so we can act on it.     Semaphore Xbox = Semaphore.OpenExisting("Xbox");     // Get the data object.     Data data = (Data)info;     // Each player notifies the Xbox he wants to play     Console.WriteLine("{0} is waiting to play!", data._handle);     // They wait on the Xbox (semaphore) until it lets them     // have a controller.     Xbox.WaitOne( );     // The Xbox has chosen the player! (Or the semaphore has     // allowed access to the resource…)     Console.WriteLine("{0} has been chosen to play. " +         "Welcome to your doom {0}. >:)", data._handle);     // Figure out a random value for how long the player lasts.     System.Random rand = new Random(500);     int timeTillDeath = rand.Next(100, 1000);     // Simulate the player as busy playing till they die.     Thread.Sleep(timeTillDeath);     // Figure out how they died.     rand = new Random( );     int deathIndex = rand.Next(6);     // Notify of the player's passing.     Console.WriteLine("{0} has {1} and gives way to another player",         data._handle, _deaths[deathIndex]);     // If all ports are open, everyone has played and the game is over.     int semaphoreCount = Xbox.Release( );     if (semaphoreCount == 3)     {         Console.WriteLine("Thank you for playing, the game has ended.");         // Set the GameOver event.         data._GameOver.Set( );         // Close out the semaphore.         Xbox.Close( );     } } 

When the Play method is run, output similar to the following is generated:

 Igor is waiting to play! AxeMan is waiting to play! Frosty is waiting to play! Dr. Death is waiting to play! HaPpyCaMpEr is waiting to play! FragMan is waiting to play! Executioner is waiting to play! Stoney is waiting to play! Beatdown is waiting to play! Xbox initializing… Halo2 loaded & ready, allowing 4 players in now… Frosty has been chosen to play. Welcome to your doom Frosty. >:) HaPpyCaMpEr has been chosen to play. Welcome to your doom HaPpyCaMpEr. >:) FragMan has been chosen to play. Welcome to your doom FragMan. >:) Dr. Death has been chosen to play. Welcome to your doom Dr. Death. >:) Frosty has fallen to their death and gives way to another player. Executioner has been chosen to play. Welcome to your doom Executioner. >:) HaPpyCaMpEr has died of lead poisoning and gives way to another player. Beatdown has been chosen to play. Welcome to your doom Beatdown. >:) FragMan has shot their own foot and gives way to another player. AxeMan has been chosen to play. Welcome to your doom AxeMan. >:) Dr. Death has was captured and gives way to another player. Stoney has been chosen to play. Welcome to your doom Stoney. >:) Executioner has choked on a rocket and gives way to another player. Igor has been chosen to play. Welcome to your doom Igor. >:) Beatdown has fallen to their death and gives way to another player. AxeMan has died of lead poisoning and gives way to another player. Stoney has bought the farm and gives way to another player. Igor has choked on a rocket and gives way to another player. Thank you for playing, the game has ended. 

Discussion

Semaphores are a new piece of the Framework in 2.0 and a welcome one. They are primarily used for resource counting and are available cross-process when named (as they are based on the underlying kernel semaphore object). Cross-process may not sound too exciting to many .NET developers until they realize that cross-process also means cross-appdomain. If you are creating additional appdomains to perform work in, say, for instance, to hold assemblies you are loading dynamically that you don't want to stick around for the whole life of your main appdomain, the semaphore can help you keep track of how many are loaded at a time. Being able to control access up to a certain number of users can be useful in many scenarios (socket programming, custom thread pools, etc.).

See Also

See the "Semaphore," "ManualResetEvent," and "ParameterizedThreadStart" topics in the MSDN documentation.



C# Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2004
Pages: 424

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net