我正在实现一个数据库访问器的派生类,它应该只允许一个线程一次访问数据库。 但是这个问题应该适用于使用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是正确的实现。
我已经尝试了两种实现,并且都工作;但我不确定哪一个会继续工作。
你能解释为什么这个或那个。
答案 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()方法。