A Thread-Safe IDisposable Base Class
Davy Brion is in the same situation as me. Every time we need to implement IDisposable (rarely) we have to look up the recommended pattern. Therefore he came up with an abstract base class that provides the proper plumbing and method signatures. All it takes is deriving from this utility class and overriding the DisposeManagedResources() method, and you have your IDisposable class. Obviously deriving from a utility class does not fit every scenario, but it works great for most of my needs.
I liked the idea so much that I decided to take it a bit further and make it ‘Thread-Safe’. In other words, any thread can call Dispose() on an object that derives from the following ManagedDisposable class, and the dispose plumbing will only execute once. The class is not actually thread-safe
The ManagedDisposable Implementation:
public abstract class ManagedDisposable : IDisposable
{
private int _disposed;
protected ManagedDisposable()
{
this._disposed = 0;
}
public bool IsDisposed
{
get
{
return this._disposed == 1;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (Interlocked.CompareExchange(ref this._disposed, 1, 0) == 0)
{
if (disposing)
{
DisposeManagedResources();
}
DisposeUnmanagedResources();
}
}
/// <summary>
/// Helper method so subclasses can easily throw if disposed
/// </summary>
protected void ThrowIfDisposed()
{
if (this.IsDisposed == true)
{
throw new ObjectDisposedException(this.GetType().FullName);
}
}
protected virtual void DisposeManagedResources()
{
}
protected virtual void DisposeUnmanagedResources()
{
}
}
Derived Class Example:
public class MyThreadingObject : ManagedDisposable
{
private SharedResource _sharedResource;
public MyThreadingObject()
{
}
public void Start()
{
Thread thread = new Thread(ThreadMethod);
thread.Start();
}
private void ThreadMethod()
{
this._sharedResource = new SharedResource();
for (int i = 0; i < 10; ++i)
{
SharedResource sharedResource = this._sharedResource;
if (sharedResource != null)
{
lock (sharedResource)
{
//do some work
//sharedResource.Process();
}
}
Thread.Sleep(1000); //do some other work
}
}
protected override void DisposeManagedResources()
{
SharedResource sharedResource = this._sharedResource;
if (sharedResource != null)
{
lock (sharedResource)
{
sharedResource.Dispose();
this._sharedResource = null;
}
}
}
}
Notice how there is another element to making the implementation thread-safe. Derived classes still need to protect access to their shared resource(s) via a lock. In the case above, a lock is acquired each time the SharedResource is accessed. If such a lock did not exist, then a race condition could exist where the DisposeManagedResources() is called from another thread (disposing and setting the _sharedResource to null) while the ThreadMethod() attempts to use the resource.
Some may also have noticed how there is no finalizer method in the ManagedDisposable. This is because there are significant costs associated with finalization. If a finalizer is needed, use this class instead:
UnManagedDisposable Implementation
public abstract class UnManagedDisposable : ManagedDisposable
{
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~UnManagedDisposable()
{
Dispose(false);
}
}
Ultimately if you are using unmanaged resources you can still use the first implementation, it just means the developer is completely responsible for calling the Dispose() method to release both managed and unmanaged resources. Otherwise the unmanaged resources will be leaked. If the second class is used, then even if a developer does not call Dispose() the destructor will eventually be called [Dispose(false) which will call DisposeUnmanagedResources()] by the garbage collector. A quick performance test indicated that ManagedDisposable is 5 times faster than UnManagedDisposable (when Dispose is not called).
An example of something that would ideally implement the UnManagedDisposable is the .NET Font class. If you use fonts and do not call Dispose(), the garbage collector will clean up after you eventually and free the unmanaged font resources. Really fonts should be disposed once they are no longer in use to immediately free these unmanaged resources, but the truth is thousands of developers and components do not always follow this suggestion.
If you don’t like two classes, just copy thr destructor code into the ManagedDisposable (and perhaps give it a better name).
Resources