开发者

How to create a thread-safe single instance of an IDisposable?

开发者 https://www.devze.com 2022-12-09 05:08 出处:网络
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

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 a开发者_如何学编程wkward too, but the instance isn't inherently thread-safe so there's nothing I can do about it.

How would you implement this?


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号