Search code examples
.netthread-safetydisposeidisposable

Thread-Safety of Dispose methods?


MSDN documents the thread-safety of instances members of BCL types pretty well, but I have never really seen information indicating how the Dispose method of IDisposable types can be called.

Is the Dispose method a) guaranteed to be thread-safe for all classes, b) never guaranteed to be thread-safe, c) guaranteed to be thread-safe for some classes (if so, where is this specifically documented)?

Finally, if the Dispose method is guaranteed to be thread-safe, does that mean I have to put a lock around each instance method in the class that uses disposable resources?

Side point: I'm aware that finalizers for types ought to be thread-safe due to the way garbage collection works in .NET (quite aggressively), and they may potentially call the Dispose method. However, let's leave this issue aside for the point here.


Solution

  • The issue of thread-safety and Dispose is somewhat tricky. Since in many cases the only thing that any thread may legitimately do with an object once any other thread has started to dispose it is attempt to Dispose it itself, it would at first blush seem like the only thing necessary to ensure thread safety would be to use Interlocked.Exchange on a 'disposed' flag to ensure that one thread's Dispose attempt happens and the other is silently ignored. Indeed, that's a good starting point, and I think it should have been part of the standard Dispose pattern (the CompareExchange should have been done in the sealed base-class wrapper method, to avoid the need for every derived class to use its own private disposed flag). Unfortunately, if one considers what Dispose actually does, things are much more complicated.

    The real purpose of Dispose is not to do something to the object being disposed, but rather to clean up other entities to which that object holds references. These entities may be managed objects, system objects, or something else entirely; they may not even be on the same computer as the object being disposed. For Dispose to be thread-safe, those other entities would to allow Dispose to clean them up at the same time as other threads might be doing other things with them. Some objects can handle such usage; others cannot.

    One particular vexing example: Objects are allowed to have events with RemoveHandler methods that are not thread-safe. Consequently, any Dispose method which cleans up event handlers should only be called from the same thread as the one in which the subscriptions were created.