1

I am studying mutual exclusion in college, and decided to write a few producer/consumer programs. I wrote a busy waiting program, a monitor program, a semaphore program, and a mutex program. They all appear to work correctly; however, the first three programs generally run until the buffer is full/empty, then switch threads and continue the process. The mutex solution as a general rule switches threads every time a value is produced/consumed, although I have tested it and this is not required - each thread can run independently. Also, it will occasionally crash, but then if I run it again, it will be fine. Does anyone know why this runs this way? Am I doing something wrong here? This is my code:

class Program
{
    const int BuffSize = 10;
    char[] Buffer = new char[BuffSize];
    volatile int Avail = 0;
    int ValuesToProduce = 95;
    Mutex _Buffer = new Mutex(false);
    Mutex IsFull = new Mutex(true);
    Mutex IsEmpty = new Mutex(true);

    public void Produce()
    {
        for (int i = 0; i < ValuesToProduce; i++)
        {
            while (Avail == BuffSize)
            {
                Console.WriteLine("Wait Producer:");
                IsFull.WaitOne(1000);
            }

            _Buffer.WaitOne();
            Buffer[i % BuffSize] = (char)(32 + i % 95);
            Avail++;
            Console.WriteLine("Produced: {0}", Buffer[i % BuffSize]);
            _Buffer.ReleaseMutex();

            try
            {
                IsEmpty.ReleaseMutex();
            }
            catch
            {

            }
        }
    }

    public void Consume()
    {
        for (int i = 0; i < ValuesToProduce; i++)
        {
            while (Avail < 1)
            {
                Console.WriteLine("Wait Consumer:");
                IsEmpty.WaitOne(1000);
            }

            _Buffer.WaitOne();
            char c = Buffer[i % BuffSize];
            Avail--;
            Console.WriteLine("Consumed: {0}", Buffer[i % BuffSize]);
            _Buffer.ReleaseMutex();

            try
            {
                IsFull.ReleaseMutex();
            }
            catch
            {

            }
        }
    }
}

class Test
{
    static void Main(string[] args)
    {
        Program program = new Program();
        Thread p = new Thread(new ThreadStart(program.Produce));
        Thread c = new Thread(new ThreadStart(program.Consume));
        p.Start();
        c.Start();
    }
}

Edit:

As proof that it is running through completion, here is a screenshot of the output:

enter image description here

  • 1
    To start with, your Main() starts two threads and then exits. Since the entry thread isn't waiting, your console app is likely just closing, and the threads are being aborted in the middle of their work. – Brian Rudolph Nov 13 '14 at 19:30
  • @BrianRudolph No, it completes its task correctly. I have run it several times in Visual Studio. All of my methods so far have done this. Is there something I should be doing to force it to wait until they are done without specifying a specific time wait? –  Nov 13 '14 at 19:33
  • I respect that it appears to complete, but that is likely just a timing bonus in that it completes before the threads abort. You are declaring managed threads inside of a function, the moment you leave that function, the CLR is attempting to clean up that stack and disposing those threads. Add a Console.ReadLine() to the bottom of Main() – Brian Rudolph Nov 13 '14 at 19:41
  • 1
    @Brian: The threads are not marked as `Background`, so they will prevent the program from exiting until they finish. The CLR doesn't try to abort them. It isn't very good style to have work going on after main exits, though... – Cameron Nov 13 '14 at 19:42
  • OK, thanks for the tips and comments; I will change this. Do you know why they alternate like this instead of running for a while or until the buffer is full? –  Nov 13 '14 at 19:44
  • I stand corrected. I guess it will wait until those threads close. Learn something new every day. – Brian Rudolph Nov 13 '14 at 19:47
  • @hosch250 Reading [this](http://stackoverflow.com/questions/7841390/how-to-use-a-mutex) may also help. – L.B Nov 13 '14 at 19:53
  • When it crashes, what's the exception~? – Blorgbeard Nov 13 '14 at 20:05
  • @Blorgbeard I do not remember for sure, it hasn't crashed for a while, and the problem may be fixed now. I am pretty sure it was something about an unsychronized value - the same exception you get when you use a Monitor without locking it. –  Nov 13 '14 at 20:06
  • @L.B Thanks. I just read that, and I will read it again later when I have my IDE open to ensure I understand it. –  Nov 13 '14 at 20:07
  • Your program is full of data races. For example, you access the shared variable `Avail` from multiple threads without synchronization. This is not supported in C#; shared variables accessed from multiple threads need to be marked `volatile` (unless you do some other synchronization before accessing them). – Cameron Nov 13 '14 at 20:13
  • @Cameron I thought the int `Avail` was protected by the mutex `_Buffer`? Is my use of the mutex wrong? –  Nov 13 '14 at 21:07
  • 1
    @hosch250: I haven't perused your code in detail, but as far I as I can see both `Produce` and `Consume` read `Avail` in their first `while` loops, which aren't protected by a mutex as far as I can see. Because `Avail` isn't `volatile`, the [JITter is free to assume those values don't change](http://stackoverflow.com/questions/24761413/why-does-a-method-call-flush-the-nonvolatile-variables-value-to-the-main-thread) during the loop and never re-read them from the backing property. – Cameron Nov 13 '14 at 21:44
  • @Cameron Oh yes, I had gotten a tip about that from my busy-waiting solution, I was getting a little mixed about it though. Thanks for the tip. I will change this. –  Nov 13 '14 at 22:20
  • 1
    For production purposes; just use a `BlockingCollection` .NET has already solved this problem... http://msdn.microsoft.com/en-us/library/dd267312(v=vs.110).aspx – BradleyDotNET Nov 13 '14 at 22:25
  • @BradleyDotNET Yes, I have recently heard of that. However, right now I am not using production code, I am just trying to learn how to use mutexes and mutual exclusion with the Producer/Consumer problem as the medium to do so. –  Nov 13 '14 at 22:28
  • No worries, just wanted to include it so you knew, and so future users would know as well. – BradleyDotNET Nov 13 '14 at 22:29
  • @hosch250: Is there a reason you avoided the typical C# `lock (syncRoot)` idiom and went with three manually-signalled mutexes instead? – Cameron Nov 13 '14 at 22:29
  • @Cameron I thought `lock(...)` was for monitors, not mutexes. I did use `lock(...)` in my monitor solution. –  Nov 13 '14 at 22:30
  • @hosch250: Monitors do provide mutual exclusion, though -- I don't quite see the benefit of using a `Mutex` here? See also http://stackoverflow.com/questions/1164038/monitor-vs-mutex-in-c-sharp – Cameron Nov 13 '14 at 22:32
  • @Cameron There is no purpose except that I want to learn how to use them. This is purely a learning exercise that I decided to do because we learned about them in our textbook. –  Nov 13 '14 at 22:34

0 Answers0