Appreciating the System ThreadPool - ' Getting Control Over Threads ' (
Page 2 of 4 )
Setting up and tearing down a thread is cheaper than setting up and tearing down
a process, but neither is free.
The ThreadPool is ideal when you want to run some code asynchronously, and you don't need any special control
over the thread, because the ThreadPool reduces thread creation costs by reusing threads.
It's easy to start a new thread. You just pass a delegate to the Thread
constructor and call Start on the new Thread instance.
With C# 2.0 anonymous methods, you don't even need to define a special method to run in the thread. You can just say:
string Source;
Thread ReadMe = new Thread(delegate()
{
using (StreamReader Reader = new StreamReader(@"..\..\Program.cs"))
Source = Reader.ReadToEnd();
});
ReadMe.Start();
...to launch a thread that (in a Visual Studio environment) reads the process's
main Program file to the Source string. After starting this up, you
can do some other setup, and then do ReadMe.Join() before you first
read the Source value.
When you create a thread explicitly, you have full power over it. You can change
the thread's Priority. You can Interrupt a blocked
thread, if necessary, or you can spawn several threads, do some other setup, and
then block until all your agents finish. You can also block on any of
several wait types, with a time-out, which allows you to do things like handle
stalled web requests by resending and hoping that load-balancing gives you a
snappy response this time.
Threads you explicitly create are foreground threads; their IsBackground
property is false. You can change IsBackground at any
time, not just before calling Start. (This is also true of thread
Priority.) The only time the IsBackground property really matters
is when a foreground thread terminates: if any and all remaining threads are
background threads, the process aborts them and then the process shuts down.
That is, .NET threads are divided into foreground and background threads. This
has nothing to do with thread scheduling: while a workstation OS may give higher
priority to the application with the UI focus than it gives to other
applications, foreground threads do not execute before background threads.
Rather, the difference between foreground and background threads is that a
process will stay alive as long as it contains at least one active running
foreground thread, while a process will terminate when its last foreground
thread terminates, no matter how many background threads are still running.
When the last foreground thread terminates, any background threads are
terminated by calling their Thread.Abort instance method. Aborting
a thread immediately raises an exception in that thread. This normally allows
threads to clean themselves up gracefully, by unwinding any finally
blocks on the stack. However, it's not impossible for an Abort to
occur within a finally block, which may have consequences like
leaving files un-flushed.
So, manually creating threads gives you a high level of control of the thread.
But you don't need that level of control in every application. And, while
creating a Thread is cheaper than creating a process, it's not free.
The overhead is pretty insignificant when a thread will consume several seconds
(or many hours) of CPU cycles. But, with a thread routine as short and simple as
the above, thread setup and teardown may take nearly as many CPU cycles as the
thread routine itself. (Remember, while disk IO may take milliseconds, the
overwhelming majority of that is spent waiting for interrupts from the disk
controller.)