【问题标题】:Regarding WaitOne() method of Mutex Type关于 Mutex 类型的 WaitOne() 方法
【发布时间】:2013-02-14 04:46:31
【问题描述】:

我写了一小段代码。 像下面的东西

    public static void SetLicence1()
    {
            Console.WriteLine("Setting Aspose Licence in Thread1 ");
            Console.WriteLine(SetAsposeLicense());
    }

    public static void SetLicence2()
    {
        Console.WriteLine("Setting Aspose Licence in Thread2 ");
        Console.WriteLine(SetAsposeLicense());
    }

    public static bool SetAsposeLicense()
    {
        try
        {
            //Declare Mutex variable:            
            using (Mutex mutex = new System.Threading.Mutex(false, "Test"))
            {
                    mutex.WaitOne(TimeSpan.FromSeconds(5));
                    var objLic = new License();
                    objLic.SetLicense(@"C:\Nivedita\License\Aspose.Cells.lic");
                    mutex.ReleaseMutex();
            }
            return true;
        }
        catch(Exception ex)
        {
               Console.WriteLine(ex.StackTrace);               
               return false;
        }
    }       
}

public class TestClass
{
    public static void Main()
    {
        Thread tid1 = new Thread(new ThreadStart(ThreadClass.SetLicence1));
        Thread tid2 = new Thread(new ThreadStart(ThreadClass.SetLicence2));

        tid1.Start();
        tid2.Start();

        Console.Read();
    }
}

这段代码运行良好。但在这里我的问题是,WaitOne() 方法是否有可能卡在进程中或跨进程而互斥对象没有被释放?虽然我用过 mutex.ReleaseMutex()。

【问题讨论】:

  • 我发现你不检查 WaitOne 的结果很不寻常。你关心它是否超时或被中断?否则,在我看来你在这个简单的例子中很好。但是,如果您在超时的情况下调用 WaitOne,则检查结果似乎是谨慎的
  • 它永远不会在 WaitOne() 调用中冻结,您的互斥锁完全不起作用。每个线程都会创建自己的互斥体,因此没有什么可以阻止它。当两个线程同时分配相同的命名互斥锁时,可能会发生崩溃。您不妨删除代码。
  • 感谢您的回复。但是我为进程间通信创建了命名互斥体。
  • 是的,我担心 waitOne 是否被中断或卡在某个地方。我将放置检查waitOne结果的代码..但在这里我想知道是否有任何东西将WaitOne置于一个永远不会被释放的循环中,以防我没有使用Timeout with WaitOne方法
  • @HansPassant “每个线程都创建自己的互斥体,因此没有什么可以阻止它。当两个线程同时分配同一个命名的互斥体时,可能会发生崩溃”?

标签: c# mutex waitone


【解决方案1】:

首先,你的意图不是很清楚。如果你只是想确保两个线程不能同时设置许可证,你需要这样的东西:

static object s_lock = new object();

public static bool SetAsposeLicense()
{
    try
    {
        lock (s_lock)
        {
            var objLic = new License();
            objLic.SetLicense(@"C:\Nivedita\License\Aspose.Cells.lic");

        }
        return true;
    }
    catch(Exception ex)
    {
           Console.WriteLine(ex.StackTrace);               
           return false;
    }
}       

您注意到这里没有 5 秒超时。如果您想等待 5 秒钟并设置许可证 不管 如果另一个线程已经完成(基本上是您问题中的代码所做的),而不是弄乱互斥锁,您最好这样做(但我很难理解,你为什么要这个):

private static object s_lock = new object();

public static bool SetAsposeLicense()
{
    if (Monitor.TryEnter(s_lock, TimeSpan.FromSeconds(5)))
    {
        try 
        {
            return SetLicenseInternal(); 
        }
        finally 
        {
            Monitor.Exit(s_lock);
        }
    }
    return SetLicenseInternal(); 
}

public static bool SetLicenseInternal()
{
    try
    {
        var objLic = new License();
        objLic.SetLicense(@"C:\Nivedita\License\Aspose.Cells.lic");
        return true;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.StackTrace);
        return false;
    }
}

使用 Monitor 对象是比使用互斥锁更“原生”的方法,它更适合这里,因为您不需要跨进程。

就 Mutex 而言,它是针对 .NET 的 system Mutex object 的包装器。命名互斥体是系统范围的并且跨进程可见。当您创建 .NET Mutex 对象并提供名称时,如果具有此名称的系统 Mutex 不存在,则会创建并包装到您获得的 .NET 对象中。如果之前已经创建了具有此名称的系统 Mutex,则此现有 Mutex 将被包装并作为新的 .NET Mutex 对象返回。

我认为你不应该在你的场景中使用互斥锁。

【讨论】:

    【解决方案2】:

    WaitOne 将抛出 AbandondMutexException 如果持有它的进程退出而不释放它。即使使用 try/finally 块,如果您在 Visual Studio 的错误位置停止调试或使用任务管理器结束进程,仍然可能发生这种情况,因此您应该重新锁定互斥锁或退出应用程序来处理这种情况。请注意,如果由于抛出的异常而未获取锁,则对 release 的调用也会抛出。

    【讨论】:

      【解决方案3】:

      编辑:我知道这个答案是不正确的。 zespri 解释如下。


      mutex 不可能不被释放,因为它会在 using 块结束后立即释放。没有其他东西可以看到mutex,因为它的范围是方法。意识到这里有两个独立的Mutex 对象,一个在 Thread1 中,一个在 Thread2 中。这只是对Mutex 的错误使用。

      如果您需要 Mutex,请尝试以下操作:

      private static Mutex mutex = new Mutex(false, "Test");
      
      public static bool SetAsposeLicense()
      {
          try
          {
              if (!mutex.WaitOne(TimeSpan.FromSeconds(5))
              {
                  throw new TimeoutException("Aspose license registration timed out.");
              }
              try
              {
                  var objLic = new License();
                  objLic.SetLicense(@"C:\Nivedita\License\Aspose.Cells.lic");
                  return true;
              }
              finally
              {
                  mutex.ReleaseMutex();
              }
          }
          catch(Exception ex)
          {
              Console.WriteLine(ex.StackTrace);
              return false;
          }
      }
      

      变化:

      • mutex 设为类成员,以便所有线程都可以看到相同的Mutex

      • 检查mutex是否被释放,或者是否超时。

      • 添加嵌套的try/finally 块以确保在设置许可证引发异常时释放mutex。之所以需要嵌套,是因为ReleaseMetux()只能被成功调用WaitOne()的线程调用。

      【讨论】:

      • 您还需要将mutex 设为静态。或者将类的一个实例传递给您的静态 SetAsposeLicense 方法并在 inst.mutex 上调用它。
      • 谢谢!我错过了看到 static 关键字。
      • Erm... 快速健全性测试,您认为这段代码会打印出什么? gist.github.com/AndrewSav/4957694
      • Mutex 类只是包装了一个系统互斥对象。它们不仅在线程中可见,而且在进程中也可见(喘气!)。当您创建一个 Mutex 对象并为其传递一个已经存在的系统互斥锁的名称时,您会得到一个新的 .NET 互斥锁对象,但它将针对同一个系统互斥锁进行包装。即使系统互斥锁是在不同的线程或进程中创建的。这基本上就是这些对象背后的想法。我认为所有这些关于使其成为班级成员的言论都具有极大的误导性。这不是他们应该的工作方式。
      • 谢谢!我刚刚仔细阅读了MSDN documentation 以确认这一点。
      猜你喜欢
      • 1970-01-01
      • 2021-11-11
      • 1970-01-01
      • 1970-01-01
      • 2013-05-13
      • 1970-01-01
      • 2017-11-01
      • 2012-03-04
      • 1970-01-01
      相关资源
      最近更新 更多