我不明白为什么.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编程范式的其余部分吗?如果/当开发人员明确销毁锁定的互斥锁时......只有将其标记为已放弃才有意义。
答案 0 :(得分:2)
您可能不希望它以您建议的方式工作。假设你有这个:
using (var m = new Mutex(....))
{
m.WaitOne();
// do some stuff here
// that ends up throwing an exception
}
当您的线程持有互斥锁时抛出异常。如果互斥锁是作为dispose的一部分释放的,那么其他一些线程可以获取互斥锁并开始参与您正在更新的数据。除了现在数据处于未知(可能不一致或损坏)的状态。
当然,最好处理异常并清理一些东西,但缺席的是我宁愿保持互斥锁(如果线程死掉也会被放弃),以便尝试获取互斥锁的下一个线程知道发生了一件坏事。
除了上述内容之外,添加自动发布还需要.NET包装器跟踪哪个线程拥有互斥锁。并且Dispose
方法必须检查该值以确定是否应该调用ReleaseMutex
。而且无法跟踪这一点。 .NET程序可以将互斥锁句柄传递给一些非托管代码,这些代码可以在没有包装器知识的情况下获取或释放互斥锁。
所以,答案是双重的:首先,这是不可能的。其次,即使有可能,你可能也不希望这种行为。