The start parameter (which is server.Serve in your case) in the thread constructor is a delegate which you knew already.
Presumably, if the Thread object
itself stays alive, then the Server
object will live, because the thread
holds a reference to the delegate
which holds a reference to the target
object
This is what C# in Depth by Jon Skeet has to say about the life of the target of a delegate
It’s worth being aware that a delegate
instance will prevent its target from
being garbage collected, if the
delegate instance itself can’t be
collected.
So yes server
won't be collected as long as the var thread
is in scope. Hoever if var thread
goes out of scope before thread.start is called (and there's no other references) then yes it can be collected.
Now the big question. Thread.Start() is called and the thread has gone out of scope before server.Serve
has completed can the GC collect the thread.
Rather than dig around lets just test it
class Program
{
static void Main(string[] args)
{
test();
GC.Collect(2);
GC.WaitForPendingFinalizers();
Console.WriteLine("test is over");
}
static void test()
{
var thread = new Thread(() => {
long i = 0;
while (true)
{
i++;
Console.WriteLine("test {0} {1} {2} ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId i);
Thread.Sleep(1000); //this is a demo so its okay
}
});
thread.Name = "MyThread";
thread.Start();
}
}
This is the output. (after adding the threadID)
test MyThread 3 1
test is over
test MyThread 3 2
test MyThread 3 3
test MyThread 3 4
test MyThread 3 5
So I call a method that creates a thread and starts it. The method ends so the var thread
is out of scope. But even though I've induced a GC and the thread variable is out of scope the thread keeps running.
So as long as you Start the thread before it goes out of scope everything will work as expected
Update
To clarify GC.Collect
Forces an immediate garbage collection of all generations.
Anything that can be collected will be. This is not as the comments suggests "little more than an expression of intent". (If it were there would be no reason for the caution against calling it) However I added the argument "2" to make sure it was gen0 through gen2.
Your point about the finalizers was worth noting however. So I added GC.WaitForPendingFinalizers which
... suspends the current thread until the thread that is processing the queue of finalizers has emptied that queue.
This means that any finalizers that needed to be processed have indeed been processed.
The point of the sample was that as long as you start the thread it will run until its aborted or finished and that the GC won't somehow abort the thread just because the var thread
has gone out of scope.
As an aside
Al Kepp is correct that indeed once you start the thread the System.Threading.Thread is rooted. You can find this out by using the SOS extension.
e.g.
!do 0113bf40
Name: System.Threading.Thread
MethodTable: 79b9ffcc
EEClass: 798d8ed8
Size: 48(0x30) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
MT Field Offset Type VT Attr Value Name
79b88a28 4000720 4 ....Contexts.Context 0 instance aaef947d00000000 m_Context
79b9b468 4000721 8 ....ExecutionContext 0 instance aaef947d00000000 m_ExecutionContext
79b9f9ac 4000722 c System.String 0 instance 0113bf00 m_Name
79b9fe80 4000723 10 System.Delegate 0 instance 0113bf84 m_Delegate
79ba63a4 4000724 14 ...ation.CultureInfo 0 instance aaef947d00000000 m_CurrentCulture
79ba63a4 4000725 18 ...ation.CultureInfo 0 instance aaef947d00000000 m_CurrentUICulture
79b9f5e8 4000726 1c System.Object 0 instance aaef947d00000000 m_ThreadStartArg
79b9aa2c 4000727 20 System.IntPtr 1 instance 001D9238 DONT_USE_InternalThread
79ba2978 4000728 24 System.Int32 1 instance 2 m_Priority
79ba2978 4000729 28 System.Int32 1 instance 3 m_ManagedThreadId
79b8b71c 400072a 18c ...LocalDataStoreMgr 0 shared static s_LocalDataStoreMgr
>> Domain:Value 0017fd80:NotInit <<
79b8e2d8 400072b c ...alDataStoreHolder 0 shared TLstatic s_LocalDataStore
Is this the thread with the name MyThread
!do -nofields 0113bf00
Name: System.String
MethodTable: 79b9f9ac
EEClass: 798d8bb0
Size: 30(0x1e) bytes
File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: MyThread
Why yes, it is.
What's it rooted to?
!GCRoot 0113bf40
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 7708 OSTHread 1e1c
Scan Thread 4572 OSTHread 11dc
Scan Thread 9876 OSTHread 2694
ESP:101f7ec:Root: 0113bf40(System.Threading.Thread)
ESP:101f7f4:Root: 0113bf40(System.Threading.Thread)
DOMAIN(0017FD80):HANDLE(Strong):9b11cc:Root: 0113bf40(System.Threading.Thread)
As the Production Debugging for .NET Framework Applications says it is the root.
Note that !GCRoot was run after it was started. Before it was started it didn't have the HANDLE(Strong).
If the output contains HANDLE(Strong),
a strong reference was found. This
means that the object is rooted and
cannot be garbage collected. Other
reference types can be found in the
Appendix.
For more info on how managed threads map to OS Threads (and how you can confirm it using SOS extensions) see this article by Yun Jin.