views:

28

answers:

2

I am implementing a pipeline pattern, the resource, say an image object, will go through the pipeline, and i have no idea how many clients are holding the resource, so what's the better way to dispose the resource?

+2  A: 

I think you only really have two options.

1) Someone owns that object, and controls access to it. Therefore, it must dispose of it when it has determined that no one should need it anymore. This is good old manual resource management.

2) You don't dispose it, and have to wait for the GC to collect and call the finalizer on the instance. Any object that implements IDisposable should also have a finalizer that handles the sam elogic if Dispose is not called. If you call dispose, then this extra step does not have to be taken, and GC is more efficient.

Chris
#2 is not practical, since i have lots of the resource be created, have to dispose it timely to avoid exhausting the memory.
Benny
+1  A: 

The way that springs immediately to mind is to wrap the resource inside some form of reference counting "wrapper" so that you can dispose of it when the reference count has released zero.

Something like this:

public class RefCountingWrapper<T> where T:IDisposable, new()
{
    private int referenceCount = 0;
    private T resource;

    public RefCountingWrapper(T item)
    {
        resource = item;
    }

    public T Acquire()
    {
        referenceCount++;
        return resource;
    }

    public void Release()
    {
        referenceCount--;
        if (referenceCount <= 0)
        {
            resource.Dispose();
        }
    }
}

Caveats that apply:

  • Anyone that has taken the result of Acquire and holds a reference to it will now hold a reference to something that has been disposed (that said, once they've released it, they shouldn't try and access it!).
  • This is vulnerable to people not matching their Acquire's and Release's, thus could cause others to encounter a disposed object inadvertantly. That shouldn't be a massive issue if you control all the code that utilises the resource/calls Acquire/Release though.
  • You'll probably want to put some locking round the Acquire/Release methods if there's even the slightest hint of multi-threaded code going near something wrapped by this.
  • There's absolutely nothing to stop the code that's using the Acquired resource from calling .Dispose() on the underlying resource
Rob
Instead of Acquire/Release, you probably should return a disposable object from Acquire and have the Dispose for that object do the decrement. The programmer can then guard each use of Acquire with a using() statement, and if they mess up then at least a finalizer will come behind and clean up.
Chris
@Chris, I think you've misinterpreted the OPs intent, they want to have a disposable object that is used by multiple "things" within their code and disposed when it's no longer being used. If the item returned by Acquire is Disposed, then it's no longer usable. Unless you're suggesting a *double* wrapper, so RefCountingWrapper<T>.Acquire returns Wrapper<T> from which Wrapper<T>.Instance is used in a using() block? Though that starts to make it very complicated and more wrapper code than actual "doing the job" code..
Rob
@Rob, no, that is what I am thinking. Although it would require slightly more code for this wrapper class, it simplifies everywhere that this code is used. Reusing standard .Net idioms is better than creating a new process all together. It's not really that much code either, it's renaming "Release" to "Dispose" and then mnoving it into another class.
Chris