在 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() 方法。
背景
我正在努力实现数据库访问器的派生 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() 方法。