Do I need a finalizer when I have started alternate threads?

  • Thread starter Thread starter nickdu
  • Start date Start date
N

nickdu

I've seen in the past that if my main thread goes away and other background
threads I have started are still running, the application won't end. By the
way, I'm talking about a .NET 2.0 console application. If this is the
expected behavior does this mean that if a class creates threads then it
should contain a finalizer so that it can shutdown those threads?
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
I'll try again to explain a bit better. I'll include some snippets to help
describe.

public class EventTracing : IDisposable
{
private ManualResetEvent _shutdown = new ManualResetEvent(false);
private ManualResetEvent _changedEvent = new ManualResetEvent(false);
private Thread _watcher = null;

private void Watcher()
{
try
{
WaitHandle waits = new WaitHandle[] {this._shutdown,
this._changedEvent};
while (true)
{
int wait = WaitHandle.WaitAny(waits);
if (wait == 0)
break;
<do some stuff>
}
}
catch(Exception)
{
}
}

public void Dispose()
{
if (this._watcher != null)
{
this._shutdown.Set();
this._watcher.Join();
}
}
}

So there is a little bit of the code. I didn't know about the IsBackground
property so maybe for me the solution is to just set IsBackground = true and
I'm done. However, let's assume I haven't set that property and the consumer
of my EventTracing class has some faulty code which does not call my
Dispose() method. Then I have a foreground thread which is keeping the
application from terminating. I'm wondering whether there is a way to
resolve this by adding a finalizer.

The part I mentioned about writing a finalizer and then thinking that it
might not be valid because objects may have already been collected is this:

The .NET Thread class is somehow associated with a running thread, but the
Thread class is itself not the actual OS thread that's running. Since I
don't hand out references to this Thread instance my EventTracing class is
the only one which has a reference to it. If I added a finalizer to my
EventTracing class then when that finalizer gets called there must be no
reference to this instance of EventTracing and thus to the Thread instance,
which means the Thread instance may have already been collected, correct?
Though I see the Thread class inherits from CriticalFinalizerObject and thus
implements a finalizer. Not sure what it does in its finalizer as the code
is hidden from reflector.
--
Thanks,
Nick

(e-mail address removed)
remove "nospam" change community. to msn.com
 
Hi Nick,

First I'd say I agree with what Peter said. Now let me share some of my
thoughts.

The short answer to your question is simply a "YES". And now let's take a
closer look at the problem.

Sub problem one: the application is terminating - your Main returns, now is
there any reasons for your object and the thread created by that object
stay alive? If the thread's job is like its name - a watcher for something
- then I don't think so (or please state the reasons).

In this case, the first modification I would make is to mark the thread
created by your object a Background thread. Background threads are
identical to foreground threads, except that background threads do not
prevent a process from terminating. Once all foreground threads belonging
to a process have terminated, the common language runtime ends the process.
Any remaining background threads are stopped and do not complete.

To mark the background thread, set Thread's IsBackground property to true.

Sub problem two: Is the "_watcher" thread executing an instance method (not
static) of the same object (this)? If this is the case, then your object
will never be GCed until the thread terminates. Because the thread holds a
reference to "this" - it is executing an instance method of "this". Thus
even you added the Finalizer, it will not be called until the thread
terminiates.

To solve the problem, don't execute the instance method on the watcher
thread if the only way to let the method return is to set the shutdown flag
in the Dispose method called by the Finalizer, that's a dead-lock.

Sub problem three: In the private Dispose method, calling
this._watcher.Join() is not the right thing to do. What if the thread took
a long time to exit or even worse, never exists (like having a dead-lock)?
Then your Dispose method never returns. Very terrible result. If the
Dispose method is called from the Finalizer, that could mean the GC will
not likely to collect anything anymore.

To make the thread exit, call Abort() which will usually terminates the
thread - unless the thread is being suspended or it cancels the abort. See
http://msdn.microsoft.com/en-us/library/ty8d3wta.aspx for more info.

Hope these information helps.

If you could post more code about your current implementation, it would be
helpful for us helpers to have a clear picture of what's going on there.

Any further questions are welcomed.

Regards,

Jie Wang

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business days is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
That's it. The problem is the Watcher(), as long as it is running, it holds
an reference to "this", even you added the Finalizer, it will never be
called. It's a dead lock, the running thread is holding a reference to the
EventTracing instance so the Finalizer doesn't get called; and if the
Finalizer doesn't get called, the thread will keep running and holding the
reference.

Regards,

Jie Wang

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business days is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Hi Pete,

You're right, the only way to terminate a thread gracefully is to let the
thread method return by itself.

My concern is not on when the application is exiting (in this case making
the thread Background will do it), it is during the running of the
application, if the that object is supposed to be finalized, however what
if the worker thread never returns? The _watcher.Join() will block the GC
thread that is dedicated to call Finalizers; or if the Dispose method is
explicitly called from code, the caller thread will be blocked.

So I'd still use Abort(), but with other means to make things look better:

Signal the working thread to exit.

Before calling Abort, use Join(<timeout>) to wait for amount of time, give
the thread a chance to exit by itself.

If the thread didn't exit within this time, call Abort trying to terminate
the thread.

In the thread method, try...catch the ThreadAbortException, and cleanup
everything inside.

This will better ensure the thread exits when the application is still
running but the object is being disposed or finalized.

Please let me know your thinkings on that.

Best regards,

Jie Wang

Microsoft Online Community Support

Delighting our customers is our #1 priority. We welcome your comments and
suggestions about how we can improve the support we provide to you. Please
feel free to let my manager know what you think of the level of service
provided. You can send feedback directly to my manager at:
(e-mail address removed).

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

Note: MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 2 business days is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions. Issues of this
nature are best handled working with a dedicated Microsoft Support Engineer
by contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
 
Back
Top