应该在IDisposable派生类中释放单例互斥量?

时间:2015-01-27 08:25:35

标签: c# concurrency mutex semaphore idisposable

背景

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

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

MSDN's website上,它们展示了如何在派生类中使用IDisposable模式。

在这个关于best practices of SQlite db in android的答案中,建议打开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; 

问题

应该在哪里调用_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,我是' m确定他们知道最好的。

但是,选项1打开了一个线程,让它在Disposed之前访问基类(至少看起来像它)。这让我觉得选项2是正确的实现。

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

你能解释为什么这个或那个。

3 个答案:

答案 0 :(得分:2)

您应该使用第一个处理基类然后释放互斥锁的选项。

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

没有关于不同Dispose方法应该执行的顺序的规则。在MSDN文章中,他们最后会调用base.Dispose,但这只是其中一个选项。

答案 1 :(得分:2)

我要求与l3arnon的答案略有不同 虽然没有规则;始终建议您按照实例化的相反顺序处理类,通过这种方式,您将维护派生类的Dispose方法的完整性。
无论如何,在您的代码中,您没有使用命名Mutex,因此Mutex的范围在DerivedClass的范围内,即使BaseClass应该独立于base.Dispose()来自子实例的子类或命令,在这种情况下是protected override void Dispose(bool isDisposing){ if(isDisposing && !IsDisposed){ //Release all resources and mutex here IsDisposed = true; } base.Dispose(isDisposing); } 调用。 我始终建议使用MSDN中的以下模式

{{1}}

答案 2 :(得分:2)

我只发帖,因为我非常不同意这个赞成的答案:)线程赛车错误是你可以处理的最糟糕的错误。 Murphy定律很难调试,它们只会在您不调试应用程序时发生,并且每月只发生一次。您永远无法充分测试您的应用程序以100%确定您没有这样的错误,您需要所有帮助您检测它们。

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

不是唯一的原因,一般来说,你对基类'Dispose()方法知之甚少。它可能会抛出。当发生这种情况时,你真的不想“泄漏”互斥锁,这会导致更多的麻烦,因为超出你控制范围的代码不足以捕获这样的异常。

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