为什么 Microsoft 的这个示例代码会崩溃?

发布于 2024-11-17 11:18:08 字数 3491 浏览 2 评论 0原文

我想了解一些有关 .NET 中多线程的知识,并从 MSDN。它编译得很好,但在运行时崩溃。我希望微软能告诉我创建多线程的正确方法。我不明白为什么它会崩溃。有人可以帮忙吗?

// 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
   }
}

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
   }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

-残月青衣踏尘吟 2024-11-24 11:18:09

您正在学习 VS2003/.NET 1.1 时代的教程。

AbandonedMutexException 是在 .NET 2 中引入的,因此为什么如果在 .NET 2 或更高版本上使用,示例代码现在会失败。

每个线程(通过等待函数)获得的适当互斥体应在线程退出之前释放。在 1.1 代码中,如果一个线程在拥有互斥体的同时退出,则下一个等待线程将获得该互斥体,就好像没有发生任何意外一样。然而,由于这经常导致不正确的行为(例如,在更新互斥锁保护的状态时线程退出),因此对 BCL 进行了更改。

老实说,对于大多数实际的字线程场景来说,编写基于互斥锁的代码很少(如果有的话)。如果这只是一个学习练习,我建议忽略它们。如果您正在尝试解决现实场景,并且您认为互斥体可能是解决方案,那么我建议您提出一个包含详细信息的单独问题。


重写了 4 个线程函数,以便在退出之前释放适当的互斥锁。唯一真正棘手的是 t3(计算出获得了哪个互斥体):

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();
}

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文