Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this sample code from Microsoft crash? [duplicate]

I wanted to learn a little about multi threading in .NET and grabbed this sample from MSDN. It compiles fine but crashes at runtime. I was hoping Microsoft would have told me the right way to create multiple threads. I can't work out why it crashes though. Can someone help?

// Mutex.cs
// Mutex object example
using System;
using System.Threading;

public class MutexSample
{
   static Mutex gM1;
   static Mutex gM2;
   const int ITERS = 100;
   static AutoResetEvent Event1 = new AutoResetEvent(false);
   static AutoResetEvent Event2 = new AutoResetEvent(false);
   static AutoResetEvent Event3 = new AutoResetEvent(false);
   static AutoResetEvent Event4 = new AutoResetEvent(false);

   public static void Main(String[] args)
   {
      Console.WriteLine("Mutex Sample ...");

      // Create Mutex initialOwned, with name of "MyMutex".
      gM1 = new Mutex(true,"MyMutex");
      // Create Mutex initialOwned, with no name.
      gM2 = new Mutex(true);

      Console.WriteLine(" - Main Owns gM1 and gM2");

      AutoResetEvent[] evs = new AutoResetEvent[4];
      evs[0] = Event1;    // Event for t1
      evs[1] = Event2;    // Event for t2
      evs[2] = Event3;    // Event for t3
      evs[3] = Event4;    // Event for t4

      MutexSample tm = new MutexSample( );
      Thread t1 = new Thread(new ThreadStart(tm.t1Start));
      Thread t2 = new Thread(new ThreadStart(tm.t2Start));
      Thread t3 = new Thread(new ThreadStart(tm.t3Start));
      Thread t4 = new Thread(new ThreadStart(tm.t4Start));

      t1.Start( );   // Does Mutex.WaitAll(Mutex[] of gM1 and gM2)
      t2.Start( );   // Does Mutex.WaitOne(Mutex gM1)
      t3.Start( );   // Does Mutex.WaitAny(Mutex[] of gM1 and gM2)
      t4.Start( );   // Does Mutex.WaitOne(Mutex gM2)

      Thread.Sleep(2000);
      Console.WriteLine(" - Main releases gM1");
      gM1.ReleaseMutex( );  // t2 and t3 will end and signal

      Thread.Sleep(1000);
      Console.WriteLine(" - Main releases gM2");
      gM2.ReleaseMutex( );  // t1 and t4 will end and signal

      // Waiting until all four threads signal that they are done.
      WaitHandle.WaitAll(evs); 
      Console.WriteLine("... Mutex Sample");
   }

   public void t1Start( )
   {
      Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");
      Mutex[] gMs = new Mutex[2] { gM1, gM2};
      Mutex.WaitAll(gMs);  // Waits until both gM1 and gM2 are released
      Thread.Sleep(2000);
      Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
      Event1.Set( );      // AutoResetEvent.Set() flagging method is done
   }

   public void t2Start( )
   {
      Console.WriteLine("t2Start started,  gM1.WaitOne( )");
      gM1.WaitOne( );    // Waits until Mutex gM1 is released
      Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
      Event2.Set( );     // AutoResetEvent.Set() flagging method is done
   }

   public void t3Start( )
   {
      Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");
      Mutex[] gMs = new Mutex[2] { gM1, gM2};
      Mutex.WaitAny(gMs);  // Waits until either Mutex is released
      Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
      Event3.Set( );       // AutoResetEvent.Set() flagging method is done
   }

   public void t4Start( )
   {
      Console.WriteLine("t4Start started,  gM2.WaitOne( )");
      gM2.WaitOne( );   // Waits until Mutex gM2 is released
      Console.WriteLine("t4Start finished, gM2.WaitOne( )");
      Event4.Set( );    // AutoResetEvent.Set() flagging method is done
   }
}
like image 285
Roman Avatar asked Jan 20 '23 09:01

Roman


1 Answers

You're following a tutorial from the VS2003/.NET 1.1 era.

AbandonedMutexException was introduced in .NET 2, hence why the sample code now fails, if used on .NET 2 or later.

The appropriate mutexes obtained by each thread (through Wait functions) should be released before the threads exit. In 1.1 code, if a thread exits whilst owning a mutex, the next waiting thread obtains the mutex as if nothing untoward has happened. However, since this frequently leads to incorrect behaviour (e.g. a thread exited whilst updating state protected by the mutex), a change was made to the BCL.

To be honest, writing Mutex based code is rarely, if ever, what you want to do for most real word threading scenarios. If this is just a learning exercise, I'd suggest ignoring them. If you're trying to solve a real-world scenario, and you thought mutexes might be the solution, I'd suggest you ask a separate question containing the details.


Re-written the 4 thread functions to release the appropriate mutexes before exit. The only really tricky one was t3 (working out which mutex was obtained):

public void t1Start()
{
    Console.WriteLine("t1Start started,  Mutex.WaitAll(Mutex[])");
    Mutex[] gMs = new Mutex[2] { gM1, gM2 };
    Mutex.WaitAll(gMs);  // Waits until both gM1 and gM2 are released
    Thread.Sleep(2000);
    Console.WriteLine("t1Start finished, Mutex.WaitAll(Mutex[]) satisfied");
    Event1.Set();      // AutoResetEvent.Set() flagging method is done
    gM1.ReleaseMutex();
    gM2.ReleaseMutex();
}

public void t2Start()
{
    Console.WriteLine("t2Start started,  gM1.WaitOne( )");
    gM1.WaitOne();    // Waits until Mutex gM1 is released
    Console.WriteLine("t2Start finished, gM1.WaitOne( ) satisfied");
    Event2.Set();     // AutoResetEvent.Set() flagging method is done
    gM1.ReleaseMutex();
}

public void t3Start()
{
    Console.WriteLine("t3Start started,  Mutex.WaitAny(Mutex[])");
    Mutex[] gMs = new Mutex[2] { gM1, gM2 };
    int mxObtained = Mutex.WaitAny(gMs);  // Waits until either Mutex is released
    Console.WriteLine("t3Start finished, Mutex.WaitAny(Mutex[])");
    Event3.Set();       // AutoResetEvent.Set() flagging method is done
    gMs[mxObtained].ReleaseMutex();
}

public void t4Start()
{
    Console.WriteLine("t4Start started,  gM2.WaitOne( )");
    gM2.WaitOne();   // Waits until Mutex gM2 is released
    Console.WriteLine("t4Start finished, gM2.WaitOne( )");
    Event4.Set();    // AutoResetEvent.Set() flagging method is done
    gM2.ReleaseMutex();
}
like image 138
Damien_The_Unbeliever Avatar answered Jan 27 '23 22:01

Damien_The_Unbeliever