处理后为什么没有命名.NET互斥锁抛出AbandonedMutexException?

时间:2012-04-13 17:52:41

标签: .net synchronization mutex abandonedmutexexception

我不明白为什么.NET互斥体不会在其中一个等待线程中抛出AbandonedMutexException或者在调用Mutex.Dispose()时释放互斥锁。

特别是这样的代码会死锁:

public static void testCall()
{
    using (var mutex = new System.Threading.Mutex(false, "testname"))
    {
        mutex.WaitOne();
        Console.WriteLine("second call");
    }
}

public static void Main(string[] args)
{
    var thread = new System.Threading.Thread(testCall);
    using (var mutex = new System.Threading.Mutex(false, "testname"))
    {
        mutex.WaitOne();
        Console.WriteLine("first call");
        thread.Start();
        System.Threading.Thread.Sleep(new TimeSpan(0, 0, 5));
        Console.WriteLine("sleep done");
    }

    thread.Join();
}

请注意,我理解AbandonedMutexException通常来自底层的WIN32互斥锁,并且只有在拥有的线程死掉的情况下才会触发本机代码 - 我一直在编写C / C ++代码并且我完全了解底层设计。我也知道以下代码是一个简单的解决方法:

using (var mutex = new System.Threading.Mutex(false, "testname"))
{
    mutex.WaitOne();
    try
    {
        Console.WriteLine("first call");
        thread.Start();
        System.Threading.Thread.Sleep(new TimeSpan(0, 0, 1));
        Console.WriteLine("sleep done");
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

我不明白的是.NET互斥锁背后的理由是在对象被锁定时被明确处理时不强制释放。这不是更符合.NET编程范式的其余部分吗?如果/当开发人员明确销毁锁定的互斥锁时......只有将其标记为已放弃才有意义。

1 个答案:

答案 0 :(得分:2)

您可能不希望它以您建议的方式工作。假设你有这个:

using (var m = new Mutex(....))
{
    m.WaitOne();
    // do some stuff here
    // that ends up throwing an exception
}

当您的线程持有互斥锁时抛出异常。如果互斥锁是作为dispose的一部分释放的,那么其他一些线程可以获取互斥锁并开始参与您正在更新的数据。除了现在数据处于未知(可能不一致或损坏)的状态。

当然,最好处理异常并清理一些东西,但缺席的是我宁愿保持互斥锁(如果线程死掉也会被放弃),以便尝试获取互斥锁的下一个线程知道发生了一件坏事。

除了上述内容之外,添加自动发布还需要.NET包装器跟踪哪个线程拥有互斥锁。并且Dispose方法必须检查该值以确定是否应该调用ReleaseMutex。而且无法跟踪这一点。 .NET程序可以将互斥锁句柄传递给一些非托管代码,这些代码可以在没有包装器知识的情况下获取或释放互斥锁。

所以,答案是双重的:首先,这是不可能的。其次,即使有可能,你可能也不希望这种行为。