tags:

views:

104

answers:

3

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 ?

+2  A: 

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).

Jon Skeet
I can poll to see if my object was garbage collected, so the solution of the WeakReference is great, my class have its own log system, so I'm able to log a warning without problem.But why "One option is to use a finalizer only for debug builds" ? what's the problem to use it in a release build ? I have seen that it's dangerous to release a managed object in the finalizer, because this object may be already garbage collected. But in my case I'm sure that my thread and its dispatcher are not be garbage collected when I'm in my destructor, unless I'v already called Dispatcher.InvokeShutdown().
Nicolas Dorier
@Slashene: You certainly can use it for a release build, but you might want to make it a bit more intrusive in a debug build, popping up an assertion error or something similar.
Jon Skeet
+1  A: 

"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.

VladV
To me Thread is a managed object which wraps an unmanaged one.So I don't think it's right to access it with in my destructor :(
Nicolas Dorier
+1  A: 

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
}
IRBMe
I see, but like I said, I don't think it's really safe to "clean" a managed object inside my destructor. A destructor should release unmanaged object, not managed ones, so I don't know if it's right.
Nicolas Dorier