我有一个应用程序代码,我在其中使用互斥锁在创建对象期间同步某些代码。对象构造函数获取互斥锁,并且仅在不再需要对象时释放它,因此释放互斥锁的一个位置将在对象析构函数中。当我使用应用程序的2个实例调试代码时,第一个实例首先获取互斥锁,第二个实例坐下并等待(mut.WaitOne())。然后用户关闭第一个应用程序实例。在这个例子中,第二个实例mut.WaitOne()抛出异常:“由于被遗弃的互斥锁而等待完成。”这发生在第一个实例中调用mut.ReleaseMutex()之前(我知道它,因为它在调用MutexRelease之前在对象析构函数代码中命中了我的断点)。似乎在调用ReleaseMutex()之前释放了互斥锁,从而导致异常。我如何解决这种竞争条件?谢谢你的帮助。
public sealed class MyObject
{
static ExtDeviceDriver devDrv;
private Mutex mut = new Mutex(false,myMutex);
public MyObject()
{
mut.WaitOne();
//Thread safe code here.
devDrv = new ExtDeviceDriver();
}
~MyObject()
{
mut.ReleaseMutex();
}
}
答案 0 :(得分:4)
你的方法有缺陷;这不是你应该如何(尝试)执行同步。如果你想阻止多个应用程序实例...那么这样做,而不是这个。如果您需要同步特定的呼叫,请尽可能在最窄的范围内进行。
我仍然可以通过复制原始引用并在另一个线程上使用它进行函数调用来创建一个竞争条件。除了构造函数之外,没有任何东西实际上是同步的,除了创建多个类的实例之外,你不会阻止任何事情,如果我尝试创建第二个实例,我会遇到死锁。不是很好。
研究IDisposable
模式。终结者不是确定性的。这不是C ++,它不是析构函数,你不能依赖它来执行它。
其次,你的互斥锁应该是静态的。每个实例都获取自己的互斥锁,因此您在实例1中同步的互斥锁与实例2的互斥锁不同。这需要是共享资源。
来自文档:
废弃的互斥锁通常表示代码中存在严重错误。当线程退出但未释放互斥锁时,受互斥锁保护的数据结构可能不处于一致状态。如果可以验证数据结构的完整性,请求互斥锁所有权的下一个线程可以处理此异常并继续。
对于系统范围的互斥锁,废弃的互斥锁可能表示应用程序已突然终止(例如,使用Windows任务管理器)。
接下来就本地v系统互斥体说了这个......
互斥锁有两种类型:本地互斥锁(未命名)和命名系统互斥锁。 本地互斥锁仅存在于您的进程中。它可以被进程中的任何线程使用,该线程具有对表示互斥锁的Mutex对象的引用。每个未命名的Mutex对象代表一个单独的本地互斥锁。
听起来像你想要一个系统互斥体。如何告诉我们哪些呼叫需要同步,以便我们可以告诉你如何做到这一点?这是一个非常基本的例子:
class Foo
{
static Mutex _mut(false);
public MyObject()
{
_mut.WaitOne();
//Thread safe code here.
devDrv = new ExtDeviceDriver();
_mut.ReleaseMutex();
}
public void SomeSynchronizedMethod()
{
// synchronize this call
_mut.WaitOne();
devDrv.DoSomething();
_mut.ReleaseMutex();
}
}
答案 1 :(得分:2)
析构函数不是确定性的; CLR无法保证何时运行。相反,在您的类中实现IDisposable
,并强制调用者在using(...)
块中使用其实例。这将确保在需要时调用Dispose
的实现。
如,
public sealed class MyObject : IDisposable
{
static ExtDeviceDriver devDrv;
private Mutex mut = new Mutex(false,myMutex);
public MyObject()
{
mut.WaitOne();
//Thread safe code here.
devDrv = new ExtDeviceDriver();
}
public void Dispose() {
mut.ReleaseMutex();
}
}
然后来电者只需要这样做:
using (var x = new MyObject()) {
// etc
}
当执行流程存在using
块时,Dispose
将被调用,无论例外或其他任何内容。