在 IDisposable 派生的 class 中应该在哪里释放单例互斥体?

Where should a singleton mutex be released in an IDisposable derived class?

背景

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

Stephen Cleary's blog 中,他展示了如何使用 IDisposable 模式实现互斥锁,几乎就是这个想法。

MSDN's website 他们展示了如何在派生的 class.

中使用 IDisposable 模式

在这个关于 best practices of SQlite db in android 的回答中,建议打开 SQLite 数据库,永远不要关闭它,而是使用一种委托来维护并发;我不喜欢这个,但它让我想到了接下来的内容。

程序

基础 class 如果多个线程试图访问它会抛出异常:

class BaseClass : IDisposable

实现锁的派生class:

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

     public DerivedClass
     {
         _mutex.WaitOne();
     }

     protected override void Dispose(bool disposing)
     {
         if (disposed)
             return; 

问题

应该在哪里调用_mutex.Release()base.Dispose()?

之前还是之后

选项 1:

 _mutex.Release();
 disposed = true;
 base.Dispose(disposing);

选项2:

 disposed = true;
 base.Dispose(disposing);
 _mutex.Release();

MSDN 说在调用 base.Dispose 之前释放任何非托管对象,这让人感觉释放也应该在之前发生,就像选项 1。而且是 MSDN,我相信他们知道的最好。

但是,选项 1 打开了线程访问基础 class 之前的大门,然后它被处置(至少它看起来像)。这让我觉得选项 2 是正确的实现。

两种实现我都试过了,都有效;但我不确定哪一个会继续工作。

能否请您也解释一下为什么会出现这种情况。

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

如果你不这样做,你就是在为并发敞开大门,即使它是一个非常小的。

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

我希望与 l3arnon 的回答略有不同。
虽无规矩;始终建议您以实例化的相反顺序处理 类,这样您将保持派生的 类.
Dispose 方法的完整性 无论如何,在您的代码中您没有使用命名的 Mutex,因此 Mutex 的范围在 DerivedClass 的范围内,否则 BaseClass 应该独立于子 类 或来自子实例的命令,在这种情况下是 base.Dispose() 调用。 我总是推荐 MSDN

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

我只是 post 因为我非常不同意投票赞成的答案 :) 线程竞争错误是您可以处理的最讨厌的错误。极难调试,墨菲定律规定它们只会在您不调试应用程序时出现,并且每月仅随机出现一次。您永远无法充分测试您的应用程序以 100% 确定您没有这样的错误,您需要 所有 可以得到的帮助来检测它们。

在另一个线程仍在使用对象时释放对象是一种不幸,这种情况并不少见。因此,当发生这种情况时,您必须尽可能地获得异常。 Super-duper 重要的是首先处理 Mutex,这最大限度地提高了获得 ODE 的几率。现在你知道你有一个错误。

这不是唯一的原因,通常您对基础 class' Dispose() 方法知之甚少。它可能会抛出。当发生这种情况时,您真的不想 "leak" 互斥锁,当您无法控制的代码不够明智地捕获此类异常时,这只会造成更多麻烦。

遵循 Microsoft 指南的两个相当不错的理由。 .NET Framework 中的每个 Disposable(bool) 覆盖最后调用 base.Dispose() 方法。