views:

137

answers:

4

Suppose I have a code as follows:

int Main()
{
    if (true)
    {
       new Thread(()=>
          {
              doSomeLengthyOperation();
          }).Start();
    }
    while (true)
    {
       //do nothing
    }
}

There are 2 threads, I'm going to call the Main thread the thread that is executing the Main() function, and the thread being new'ed up inside the "if" test as Thread A.

My question is, when does Thread A get destroyed? Will doSomeLenghtyOperation() be able to run into completion?

Since there are no references pointing at Thread A, will it be marked as a candidate for garbage collection:

  1. Immediately after the "new Thread().Start()" statement itself finishes?
  2. Immediately after the "if(true)" scope is exited?
  3. After the doSomeLengthOperation() runs to finish?
  4. Never?

All the examples I see are the Main() holding the reference, and then Main thread waiting to join with thread A before exiting. I'm curious what the lifetime of the code above is.

Thanks in advance!

+2  A: 

The Thread object will be eligible for garbage collection as soon as it's not used any more, i.e. immediately after calling the Start method. (It will however not be collected immediately, as the garbage collector runs at specific times.)

The actual thread however is not relying on the Thread object, and will continue to run even if the Thread object is collected.

If the thread is still running when the main method exists, the application will not end until the thread completes, unless you have marked the thread to be a background thread.

Guffa
"The Thread object will be up for garbage collection as soon as". This is not true. GC is not deterministic. you must say "it will be a candidate for garbage collection." GC does not kick in unless certain threshold is reached.
Aliostad
If the Thread object could be collected that soon, then its finalizer could also run then. Wouldn't that conflict with the thread still running, though?
Joren
@Aliostad: That's what it means, but I will make it clearer to avoid any misunderstandings.
Guffa
@Joren: No, the finalizer doesn't stop the thread. The `Finalize` method is not overriden in the `Thread` class, which means that it won't touch the actual thread.
Guffa
+1 for update and using "Eligible" :)
Aliostad
I am still not happy with this. If the thread object is GCed, then what happens if we use the properties of the thread object itself in the LongRunning task? For example using Thread.CurrentThread.Name. I think it is very likely that Thread.CurrentThread keeps it in the memory.
Aliostad
@Aliostad: Yes, that is a good point, however if it's actually the same `Thread` object, it's still the thread keeping the `Thread` object alive, not the other way around. There is no need to keep a reference to the `Thread` object in the main program to keep the actual thread running.
Guffa
@Guffa: What do you mean, `Finalize` isn't overridden in `Thread`? Sure, it isn't, but that doesn't make Thread any less finalizable. `Thread` is even a `CriticalFinalizerObject`. My previous point was, if the `Thread` object can be GC'd, then it can be finalized. But it seems impossible that the object can be correctly finalized when the thread is still running: a finalizer is supposed to release resources, which would imply stopping the thread. Since the thread does not stop, the finalizer must not run, and therefore the GC must not collect the `Thread`. Any reason to suspect otherwise?
Joren
A: 

This is an excellent question! Thread will surely finish and you can try that yourself. But it can get interesting if you call GC.Collect() in the while. According to Richter's C# via CLR, it will be garbage collected.

UPDATE

I believe it will not be Garbage Collected since Thread.CurrentThread keeps it in the memory by having a reference.

Aliostad
The `Thread` object will most likely be collected, but that doesn't affect the actual thread.
Guffa
+5  A: 

when does Thread A get destroyed?

When doSomeLengthyOperation finishes.

Will doSomeLenghtyOperation() be able to run into completion

Yes, even if the main thread exists because it is not a background thread. If you set the IsBackground property to true before starting the thread whenever the main thread exists, this thread will also be stopped.

Darin Dimitrov
From MSDN docs: "It is not necessary to retain a reference to a Thread object once you have started the thread.The thread continues to execute until the thread procedure is complete." "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."
weismat
-1: You have to make the distinction between the `Thread` object and the actual thread, as the OP clearly asks about the `Thread` object being garbage collected. There is nothing keeping the `Thread` object alive just because the actual thread is still running.
Guffa
@Guffa - I could not find any authoritative source but I believe that running threads are considered as GC roots. Further, as each managed thread does not corresponds to unmanaged counter part, its quite possible that managed thread object may be treated as a root object it self (unless CLR uses some other internal data structure to represent managed thread which is quite possible).
VinayC
+2  A: 

Word "thread" could mean several things here:

  • System.Threading.Thread object (created by new Thread()),
  • CLR thread (magaged thread),
  • OS thread (unmanaged thread).

Thread object will be candidate for GC as soon as Start() method completes, because there are no more references to it.

Managed thread will stay alive while doSomeLengthyOperation() runs.

Quoting the article by James Kovacs, Microsoft MVP:

A managed thread's lifetime is independent of the Thread object that creates it, a very good thing given that you wouldn't want the GC to terminate a thread that was still doing work simply because you lost all references to the associated Thread object. So the GC is collecting the Thread object, but not the actual managed thread.

The article also contains some helpful code samples if you want to experiment yourself.

Operating system thread, theoretically, has no one-to-one relationship with managed threads. From MSDN:

...a sophisticated host can use the CLR Hosting API to schedule many managed threads against the same operating system thread, or to move a managed thread between different operating system threads.

In practice, however, CLR thread maps directly to a Windows thread today.

VladV