如何创建IDisposable的线程安全的单个实例?

时间:2009-10-26 11:09:27

标签: c# .net singleton thread-safety

我的Web应用程序需要这个类似DB连接的对象。创建它很慢并且很少使用,所以我只想保留它的一个实例。如果多个请求同时需要它,它们将lock()对象并序列化它们的访问权。

为了让事情更有趣,对象是IDisposable,可以关闭。当然,我会避免编写关闭它的代码,但是......你知道......比抱歉更安全,对吧?

所以,天真的方法是实现这样的事情:

private static DBClass _Instance;
private static object _DBLock = new object();

public DBClass GetDB()
{
    if ( _Instance == null )
        lock (_DBLock )
            if ( _Instance == null )
                _Instance = CreateInstance();
    lock (_Instance)
        if ( _Instance.Disposed )
            _Instance = CreateInstance();
    return _Instance;
}

然后将使用它:

lock (var Conn = Global.GetDB() )
{
    // Use Conn here.
}

这可能大部分时间都可以工作,但我看到一个可以被利用的空缺 - 如果两个线程同时调用它,它们会同时获得相同的实例,然后仍然可以{{1在另一个获得Dispose()之前它。因此 - 后来的代码失败了。在每个使用它的地方检查lock()似乎也很尴尬。事实上,Disposed它似乎也很尴尬,但实例本身并不是线程安全的,所以我无能为力。

你会如何实现这个?

1 个答案:

答案 0 :(得分:2)

首先,我建议避免双重检查锁定。很容易弄错 - 就像你在这种情况下所做的那样,不要让变量变得不稳定。

鉴于您不希望实际处置该对象,是否有任何理由不将其包装在实现相同API但未暴露处置的内容中?这样,您也可以封装锁定,具体取决于您实际使用对象的方式。另一种方法是使用Action<DBClass>公开API - 您传递要对对象执行的操作,并且帮助程序负责创建实例并适当锁定。

最后一点:就可测试性而言,所有这些都有些棘手。我不知道你是否喜欢单元测试等,但是单身人士通常会让生活变得有些尴尬。