Nicolas Dorier
Nicolas Dorier

Reputation: 7465

How to release a managed resource which will not be garbage collected if the caller forget to use Dispose()?

In my code, I have a class which create a new thread. This new thread has a Dispatcher, thus the thread will not finish unless I call

Dispatcher.CurrentDispatcher.InvokeShutdown();

The thread cannot be referenced outside of my class. So I was thinking : how to ensure that my thread finish when my object is garbage collected ?

One response is to use IDisposable, but if someone forget to call Dispose(), the thread will never stop so it's not a solution.

Another response is two use Dispose + Destructor, but I've heard that we should only use the destructor for release unmanaged resource. I'm in a dead end.

What is the best solution ?

Upvotes: 2

Views: 501

Answers (3)

IRBMe
IRBMe

Reputation: 4425

There's a lot of useful information on dispose and finalize here. Obviously if you're writing a reusable class or a library to be used by other programmers, you can't assume that the Dispose method will always be called, and so it would be wise to implement a finalizer:

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

protected virtual void Dispose(bool disposing) {
    if (disposing) {
        // Free other state (managed objects).
    }
    // Free your own state (unmanaged objects).
    // Set large fields to null.
}

~MyClass() {
    Dispose (false);
}

It seems like this is not that reusable a class you're writing, and is used pretty much within your own program. In that case, you have the following choices:

  • Resources are cleaned up in the finalizer if Dispose isn't called
  • Same as above, but a warning or error is also logged
  • If the finalizer is called, fail loudly

You can guard the last 2 from being compiled in to the release by guarding them:

~MyClass() {
    Dispose (false);
    #if DEBUG
        // log a warning or scream at the developer in some way
    #endif
}

Upvotes: 1

VladV
VladV

Reputation: 10349

"Dispose + Destructor" solution seems fine to me. You could employ some tricks, as Jon said, to detect whether a Dispose call was missed, but having the thread stopped on a finalizer call will be an additional safeguard.

By the way, a thread is an unmanaged resource.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1500375

One option is to use a finalizer only for debug builds. Don't make it just "nicely" shut down the thread: make it log a warning very prominently. That should help you to isolate places where you've forgotten to dispose of the object properly.

However, it's not foolproof and it has the undesirable property of making the debug and release code different.

Would your dispatcher normally receive events very frequently? Could you make it at least log a warning if it's received no events for a long time?

One fairly nasty option would be to make the dispatcher have a weak reference to the disposable object. It could periodically check whether the object has been garbage collected (using WeakReference.IsAlive) and shut itself down if so. Again, that's not terribly pleasant (and should log a warning).

Upvotes: 2

Related Questions