views:

172

answers:

1

There is this DB-connection-like object that my web application will need. It is pretty slow to create and will be used rarely, so I would like to keep only a single instance of it around. If several requests need it at the same time, they will lock() the object and serialize their access.

To make things more fun the object is an IDisposable and can be closed. Naturally, I'll avoid writing code that closes it, but... you know... better safe than sorry, right?

So, the naïve approach would be to implement it something like this:

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;
}

It will then be used like:

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

This will probably work most of the time, but I see an opening that could be exploited - if two threads call this at the same time, they get the same instance at the same time, and then one could still Dispose() it before the other acquires a lock() on it. Thus - later code fails. Checking for Disposed at every place that uses it also seems awkward. In fact, lock()ing it seems awkward too, but the instance isn't inherently thread-safe so there's nothing I can do about it.

How would you implement this?

+2  A: 

For a start, I'd suggest avoiding double-checked locking. It's very easy to get it wrong - as you've done in this case, by not making the variable volatile.

Given that you don't want to actually dispose the object, is there any reason not to wrap it in something that implements the same API but without exposing disposal? That way you could also encapsulate the locking, potentially, depending on how you actually use the object. Another way of doing this would be to expose an API in terms of Action<DBClass> - you pass in the action you want to take with the object, and the helper takes care of creating the instance and locking appropriately.

One final point: all of this feels somewhat tricky in terms of testability. I don't know whether you're a fan of unit testing etc, but singletons often make life somewhat awkward for testing.

Jon Skeet
The problem is that this thing returns other objects that hold a reference to it and perform operations on it. Unless I wrap a couple hundred classes, it won't work. The Action<> approach is noteworthy, but I wonder if it the increased clutter in code is worth it. I want an elegant approach, but this is kinda going the other way...
Vilx-
Btw - I'm using a lock() there anyway. Why the need for volatile?
Vilx-
It's possible that due to the *secondary* lock, you're actually okay - but given that you've got two different locks involved, it's not at all clear to me that it genuinely *is* okay. I'd personally use a single lock for everything - put the whole method in a lock statement using `_DBLock`. The subtleties of the memory model are pretty tricky. Likewise if another thread is using the object *without* locking but disposes, there's no guarantee you'll spot it and create a new instance. Basically it feels like this design is opening up room for a lot of subtle bugs.
Jon Skeet
Hmm, yes... I guess that the Action<> approach is the safest. If only you could prevent an instance from being carried outside the delegate! :D OK, I guess I'll stick with that.
Vilx-