C# IDisposable派生类中的单例互斥应该在哪里发布? 背景

C# IDisposable派生类中的单例互斥应该在哪里发布? 背景,c#,concurrency,mutex,semaphore,idisposable,C#,Concurrency,Mutex,Semaphore,Idisposable,我正在实现一个数据库访问器的派生类,该类一次只允许一个线程访问数据库。但这个问题应该适用于使用一次性模式的任何形式的单线程访问 在本文中,他展示了如何使用IDisposable paten实现互斥,这几乎就是他的想法 在上,它们展示了如何在派生类中使用IDisposable模式 在这个关于它的回答中,建议打开SQLite数据库,而不要关闭它,而是使用一种委托来维护并发性;这我不喜欢,但它给了我接下来的想法 节目 当多个线程试图访问异常时引发异常的基类: class BaseClass : IDi

我正在实现一个数据库访问器的派生类,该类一次只允许一个线程访问数据库。但这个问题应该适用于使用一次性模式的任何形式的单线程访问

在本文中,他展示了如何使用IDisposable paten实现互斥,这几乎就是他的想法

在上,它们展示了如何在派生类中使用
IDisposable
模式

在这个关于它的回答中,建议打开SQLite数据库,而不要关闭它,而是使用一种委托来维护并发性;这我不喜欢,但它给了我接下来的想法

节目 当多个线程试图访问异常时引发异常的基类:

class BaseClass : IDisposable
实现锁的派生类:

class DerivedClass : BaseClass
{
     private static Semaphore _mutex = new Semaphore(1, 5);

     public DerivedClass
     {
         _mutex.WaitOne();
     }

     protected override void Dispose(bool disposing)
     {
         if (disposed)
             return; 
问题: 应该在哪里调用
\u mutex.Release()
?在
base.Dispose()之前或之后?

i、 e.选项1:

 _mutex.Release();
 disposed = true;
 base.Dispose(disposing);
 disposed = true;
 base.Dispose(disposing);
 _mutex.Release();
选项2:

 _mutex.Release();
 disposed = true;
 base.Dispose(disposing);
 disposed = true;
 base.Dispose(disposing);
 _mutex.Release();
MSDN说在调用
base.Dispose
之前释放所有非托管对象,这让它感觉像选项1一样,释放也应该在之前发生。而且是MSDN,我相信他们知道得最好

但是,选项1在释放基类之前为线程打开了访问基类的大门(至少看起来是这样)。这让我觉得选项2是正确的实现

我已经尝试了这两种实现,并且都很有效;但我不确定哪一个会继续工作


您还可以解释一下为什么是一个或另一个。

您应该选择第二个选项,首先处理基类,然后释放互斥

如果您不这样做,那么您将为并发性敞开大门,即使这是一扇非常小的门


对于不同的
Dispose
方法的执行顺序没有规则。在MSDN文章中,他们确实在最后调用了
base.Dispose
,但这只是选项之一。

我请求与
l3arnon
的答案略有不同。
虽然没有规则;建议您按照与实例化相反的顺序处理类,这样可以保持派生类的
dispose
方法的完整性。
无论如何,在您的代码中,您没有使用命名的
互斥体
,因此
互斥体
的作用域在
派生类
的作用域内,即使在其他情况下,
基类
也应该独立于子类或来自子实例的命令,在这种情况下,
base.Dispose()
调用。 我总是推荐MSDN的以下模式

protected override void Dispose(bool isDisposing){
 if(isDisposing && !IsDisposed){
  //Release all resources and mutex here
  IsDisposed = true;
 }
 base.Dispose(isDisposing);
}

我之所以发帖,是因为我非常不同意投票结果:线程赛车漏洞是你能对付的最肮脏的漏洞。调试极其困难,墨菲定律规定,只有当你不调试你的应用程序时,它们才会发生,并且每个月只随机发生一次。你永远无法充分测试你的应用程序,以100%确定你没有这样的错误,你需要所有的帮助,你可以得到检测他们

当另一个线程仍在使用某个对象时处理该对象是一件很不幸的事情,这种情况并不少见。因此,当这种情况发生时,您必须尽可能使您得到异常。超级复制者必须首先处理互斥,这将最大限度地提高获得ODE的几率。现在你知道你有一个bug了

这不是唯一的原因,通常您对基类的Dispose()方法知之甚少。它可能会扔。当这种情况发生时,您并不真的想“泄漏”互斥体,这只会在您无法控制的代码不明智地捕获这样的异常时造成更多的麻烦


遵循微软指南有两个相当不错的理由。NET Framework中的每个一次性(bool)重写最后调用base.Dispose()方法。

是否希望在类的整个生命周期中使用互斥体?另外,它说在最后调用
base.Dispose
?@I3arnon我不太明白你的意思。但是互斥锁应该在应用程序的整个生命周期中始终存在,以允许创建DerivedClass的多个实例,并且始终只允许一个线程访问。该类始终在
中使用
上下文。@I3arnon如果您查看MSDN上的实现,它看起来好像Dispose结构希望您最后处理。是否希望您的应用程序挂起实例创建,例如
var d=new-Derived
?您应该将其从构造函数中取出,并且仅在调用一个
派生的
类方法时使用它。好的,所以没有任何特殊的原因,除了他们最后执行
Dispose
的明显原因。这很有趣,您不认为这会引入并发问题吗?不会;但同样,当您不确定是否仍然持有/使用来自其他线程的资源时,您不应该占用资源。在这种情况下,问题不在于处理,而在于不同的线程如何访问资源。开发人员需要确保所有线程的所有操作都已完成,然后才能处置实例。引用:
“在另一个线程仍在使用对象时处置对象”
-这可能是常识,但不是;现在一切都有意义了!这完美地解释了“为什么”@Barnstokkr只是想澄清一下:没有人提到处理另一个线程正在使用的对象。这里的互斥是静态的(我不建议这样做,但这与问题无关)
base.Dispose
可能会像其他方法一样抛出(尽管它不应该抛出),解决方案是一个
try finally
块。如果这个类的目的是限制其基类上的并发性(同样,不是我推荐的),那么它就忽略了它。