Search code examples
c#multithreadingsemaphorethread-synchronization

Thread programming using semaphores


I'm trying to understand how to use semaphores while working with threads.

I have 2 threads that uses the same resource - an ArrayList. One method adds a random temperature to the list, the other method calculate the average temperature of the list.

How do I use semaphore's methods Wait and Release in this context? and how can I control that my thread that calculate the average temperature starts after something is added to my list.

This is some of my code:

class Temperature
{
    private static Random random = new Random();
    private static ArrayList buffer = new ArrayList();
    static SemaphoreSlim e, b;

    public static void Main (string[] args)
    {
        e = new SemaphoreSlim(6); //how will this work?
        b = new SemaphoreSlim(1);
        Thread t1 = new Thread (Add);
        t1.Start ();
        Thread t2 = new Thread (Average);
        t2.Start ();
    }

    public static void Add()
    {
        int temperature;
        for (int i=0; i<50; i++)
        {
            temperature = random.Next (36, 42);
            Console.WriteLine ("Temperature added to buffer: " + temperature);
            b.Wait ();
            e.Wait ();
            buffer.Add(temperature);
            b.Release ();
            Thread.Sleep (50);
        }
    }
}

Solution

  • You need two things here.

    1. To prevent the threads from interfering with each other when updating the list, you need a lock or a semaphore.
    2. To make the averaging thread update the average whenever an item is added, you need it to wait on an event, and have the adding thread set that event whenever it adds an item.

    You can do the one with a semaphore, although a lock or Monitor is more appropriate. The other really requires an event, probably AutoResetEvent.

    private static Random random = new Random();
    private static ArrayList buffer = new ArrayList();
    static SemaphoreSlim BufferLock = new SemaphoreSlim(1);
    static AutoResetEvent ItemAdded = new AutoResetEvent(false);
    
    public static void Main (string[] args)
    {
        BufferLock.Wait();  // initially acquire the semaphore.
        Thread t1 = new Thread (Add);
        t1.Start ();
        Thread t2 = new Thread (Average);
        t2.Start ();
    
        // wait for adding thread to finish
        t1.Wait();
    }
    
    public static void Add()
    {
        int temperature;
        for (int i=0; i<50; i++)
        {
            temperature = random.Next (36, 42);
            BufferLock.Wait();
            buffer.Add(temperature);
            ItemAdded.Set();
            BufferLock.Release();
            Console.WriteLine ("Temperature added to buffer: " + temperature);
            Thread.Sleep (50);
        }
    }
    
    public static void Average()
    {
        while (true)
        {
            ItemAdded.Wait();  // wait for item to be added
            BufferLock.Wait();
            ComputeAverage();  // however you want to do this
            BufferLock.Release();
        }
    }
    

    If you want to make sure that the last Average is computed, you'll have to wait on t2. Of course, you'll need a way to tell the thread to exit. Look into Cancellation.

    You really shouldn't be using ArrayList. List< int > would be a much better choice. The only reason ever to use ArrayList is when you're writing code for .NET versions prior to 2.0, or when you're supporting very old code.

    You could replace the AutoResetEvent with a Semaphore to simulate the event notification, but doing so is a bit of a kludge and will require very careful coding to get right. I certainly wouldn't want to do it that way.