www.gibmonks.com

Table of Contents




Previous Page
Next Page

Recipe 18.12. Using Events to Make Threads Cooperate

Problem

You have multiple threads that need to be served by a server but only one can be served at a time.

Solution

Use an AutoResetEvent to notify each thread when it is going to be served. For example, a diner has a cook and multiple waitresses. The waitresses can keep bringing in orders, but the cook can serve up only one at a time. You can simulate this with the Cook class shown in Example 18-13.

Example 18-13. Using events to make threads cooperate

public class Cook
{
    
    public static AutoResetEvent OrderReady = new AutoResetEvent(false);

    public void CallWaitress()
    {
        // We call Set on the AutoResetEvent and don't have to
        // call Reset like we would with ManualResetEvent to fire it
        // off again. This sets the event that the waitress is waiting for
        // in PlaceOrder.
        OrderReady.Set();
    }
}

The Cook class has an AutoResetEvent called OrderReady that the cook will use to tell the waiting waitresses that an order is ready. Since there is only one order ready at a time and this is an equal opportunity diner, the waitress who has been waiting longest gets her order first. The AutoResetEvent allows for just signaling the single thread when you call Set on the OrderReady event.

The Waitress class has the PlaceOrder method that is executed by the thread. PlaceOrder takes an object parameter, which is passed in from the call to t.Start in the next code block. The Start method uses a ParameterizedThreadStart delegate, which takes an object parameter. PlaceOrder has been set up to be compatible with it. It takes the AutoResetEvent passed in and calls WaitOne to wait until the order is ready. Once the Cook fires the event enough times that this waitress is at the head of the line, the code finishes.

	public class Waitress
	{
	    public static void PlaceOrder(object signal)
	    {
	        // Cast the AutoResetEvent so the waitress can wait for the
	        // order to be ready.
	        AutoResetEvent OrderReady = (AutoResetEvent)signal;
	        // Wait for the order…
	        OrderReady.WaitOne();
	        // Order is ready….
	        Console.WriteLine("Waitress got order!");
	    }
	}

The code to run the "diner" creates a Cook and spins off the Waitress threads, then calls all waitresses when their orders are ready by calling Set on the AutoResetEvent:

	// We have a diner with a cook who can serve up only one meal at a time.
	Cook Mel = new Cook();

	// Make up five waitresses and tell them to get orders.
	for (int i = 0; i < 5; i++)
	{
	    Thread t = new Thread(Waitress.PlaceOrder);
	    // The Waitress places the order and then waits for the order.
	    t.Start(Cook.OrderReady);
	}

	// Now we can go through and let people in.
	for (int i = 0; i < 5; i++)
	{
	    // Make the waitresses wait…
	    Thread.Sleep(2000);
	    // OK, next waitress, pickup!
	    Mel.CallWaitress();
	}

Discussion

There are two types of events, AutoResetEvent and ManualResetEvent. There are two main differences between the events. The first is that AutoResetEvents release only one of the threads that are waiting on the event while a ManualResetEvent will release all of them when Set is called. The second difference is that when Set is called on an AutoResetEvent, it is automatically reset to a nonsignaled state while the ManualResetEvent is left in a signaled state until the Reset method is called.

See Also

See the "AutoResetEvent" and "ManualResetEvent" topics in the MSDN documentation and Programming Applications for Microsoft Windows, (Fourth Edition).


Previous Page
Next Page