BlackWaspTM
.NET Framework
.NET 1.1+

IDisposable and Thread Safety

The IDisposable interface should be applied to classes that hold unmanaged resources, either directly or indirectly. When developing using parallel programming or multi-threading techniques, IDisposable should be implemented in a thread-safe manner.

IDisposable Interface

IDisposable is a standard interface in the .NET framework. It is implemented by classes that hold managed resources or instances of other types that also implement IDisposable. The interface includes a single method, Dispose, which can be called to immediately release the unmanaged resources and free up memory or system resources accordingly. When implemented correctly, disposable classes also enable the garbage collector to free up those resources, preventing leaks.

If you are developing multi-threaded software or are using parallelism, the implementation of IDisposable should be thread-safe. In this article we will see how this can be achieved.

Thread-Safe IDisposable

To begin, we'll look at some code that shows a standard implementation of IDisposable but that is not thread-safe. I won't describe the implementation. If you are would like more information about the basic pattern, read the "Implementing IDisposable" article. The code below shows the sample class. It does not hold any unmanaged resources directly so no finalizer has been included. It does hold a reference to a Stream object, which is disposed of correctly in the protected Dispose method.

public class DisposableResource : IDisposable
{
    bool _disposed;
    Stream _stream;

    public DisposableResource()
    {
        _stream = File.OpenRead(@"c:\test\test.txt");
    }

    public long FileLength()
    {
        if (_disposed) throw new ObjectDisposedException(
            typeof(DisposableResource).FullName);
        return _stream.Length;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                if (_stream != null) _stream.Dispose();
            }

            _stream = null;
            _disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Adding Thread-Safety

The above implementation presents several problems when accessed by several threads concurrently. Each is a bug that would only be seen intermittently and could be difficult to find. The problems include:

  • It is possible for two threads to access the unmanaged resource at the same time. If the resource is not thread-safe this could give unexpected results.
  • It is possible for one thread to start disposing resources whilst another uses them. This would happen if both threads passed the check that _disposed is false, before one disposed of the resource and the other tried to use it. Using a disposed object or released resource could cause an exception or unexpected side effects.
  • Two threads could attempt to release the resources at the same time. In the example code this could lead to an ObjectDisposedException.
1 November 2011