A key point which is often overlooked with Dispose
is that its purpose is not to do anything to the object being disposed, but rather is to allow the object being disposed to notify outside entities that their services are no longer required. There are many situations in which an outside entity may be shared by many objects which are used by different threads. In many cases it will thus be important that the outside entity be capable of multiple notifications or requests from different threads simultaneously. If the outside entity uses a pool to manage resources, it may not need to worry about simultaneous actions being performed upon a particular pool item, but it must be prepared for the possibility that it might receive multiple simultaneous requests to allocate or release items from/to the same pool.
A second point is that certain kinds of communication libraries do not have any non-blocking methods and support exactly one multi-threaded scenario: an action which is blocking may be asynchronously aborted by any thread. Aborting an action in this fashion will leave the channel in an undefined state; the channel will not be usable again until such time as it is closed and re-opened. Because such an abort will leave the channel in a useless state, there is no reason to continue holding any resources associated with it after issuing the abort. Consequently, a common and useful pattern is to use Dispose
itself to perform the abort. Such an abort must be run from a thread separate from the object's main thread [if the object's main thread is blocked, it can't run any user code to trigger the abort!] it must be thread-safe. Note that actual cleanup behavior might not necessarily be run on the thread that calls Dispose
. If Dispose
is called while the object is "running" code on its main thread (or such code is blocked within one of the object's methods) it may set a flag to force its main thread to do the cleanup; if it's called while the object's own thread is not executing its code, the object may let the thread which is calling Dispose
become its "main thread", which could then clean it up; if its former "main thread" attempts to do something during cleanup, such action would fail immediately.
Although in most cases Dispose
shouldn't be called on an object while it's still in use, there are some situations like the above where that would represent a necessary usage scenario. Yanking a resource out from under a consumer may be a rather crude way of forcing that consumer to shut down, but in many cases it may be safer than any available alternative. Additionally, even in cases where an object shouldn't be disposed but is anyway, a Dispose
method should strive to limit the harm such disposal could cause. It's fine if calling Dispose
on an object while it's still in use causes operations on the object to fail in unpredictable fashion. It's not so fine if calling Dispose
on an one object which is still in use causes the next object created to be given resources which the first object is still using. Whether or not Dispose
behaves in truly thread-safe fashion, it should ensure that harm caused by improper usage will be limited to the object being disposed.