1

我不明白为什么 .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 编程范式的其余部分吗?如果/当开发人员显式销毁锁定的互斥锁时……只有将其标记为已放弃才有意义。

4

1 回答 1

2

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

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

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

当然,最好处理异常并清理东西,但我宁愿保持互斥锁保持不变(或者如果线程死了就放弃),以便下一个尝试获取互斥锁的线程知道发生了一些不好的事情.

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

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

于 2012-04-13T18:38:01.350 回答