Creating Threads and Passing Data at Start Time
Updated: May 2011
When an operating-system process is created, the operating system injects a thread to execute code in that process, including any original application domain. From that point on, application domains can be created and destroyed without any operating system threads necessarily being created or destroyed. If the code being executed is managed code, then a Thread object for the thread executing in the current application domain can be obtained by retrieving the static CurrentThread property of type Thread. This topic describes thread creation and discusses alternatives for passing data to the thread procedure.
Creating a new Thread object creates a new managed thread. The Thread class has constructors that take a ThreadStart delegate or a ParameterizedThreadStart delegate; the delegate wraps the method that is invoked by the new thread when you call the Start method. Calling Start more than once causes a ThreadStateException to be thrown.
When the Start method returns, the thread is in the Running state. It is ready to be scheduled for execution, but might not have begun executing.
Note
|
|---|
|
In most cases, it is not necessary to ensure that the new thread has begun executing before the thread that started it continues executing. If it is necessary, the thread that called the Start method should block (for example, by calling the Mutex.WaitOne method on a mutex that is visible to the new thread) as soon as it has started the new thread. The new thread can unblock the old thread (for example, by calling the Mutex.ReleaseMutex method on the shared mutex) as soon as it is safe for the old thread to proceed. Do not use the ThreadState and IsAlive properties to synchronize the activities of threads. |
Once a thread is started, it is not necessary to retain a reference to the Thread object. The thread continues to execute until the thread procedure ends.
The following code example creates two new threads to call instance and static methods on another object.
using System; using System.Threading; public class ServerClass { // The method that will be called when the thread is started. public void InstanceMethod() { Console.WriteLine( "ServerClass.InstanceMethod is running on another thread."); // Pause for a moment to provide a delay to make // threads more apparent. Thread.Sleep(3000); Console.WriteLine( "The instance method called by the worker thread has ended."); } public static void StaticMethod() { Console.WriteLine( "ServerClass.StaticMethod is running on another thread."); // Pause for a moment to provide a delay to make // threads more apparent. Thread.Sleep(5000); Console.WriteLine( "The static method called by the worker thread has ended."); } } public class Simple { public static void Main() { Console.WriteLine("Thread Simple Sample"); ServerClass serverObject = new ServerClass(); // Create the thread object, passing in the // serverObject.InstanceMethod method using a // ThreadStart delegate. Thread InstanceCaller = new Thread( new ThreadStart(serverObject.InstanceMethod)); // Start the thread. InstanceCaller.Start(); Console.WriteLine("The Main() thread calls this after " + "starting the new InstanceCaller thread."); // Create the thread object, passing in the // serverObject.StaticMethod method using a // ThreadStart delegate. Thread StaticCaller = new Thread( new ThreadStart(ServerClass.StaticMethod)); // Start the thread. StaticCaller.Start(); Console.WriteLine("The Main() thread calls this after " + "starting the new StaticCaller thread."); } }
In the .NET Framework version 2.0, the ParameterizedThreadStart delegate provides an easy way to pass an object containing data to a thread when you call the Thread.Start method overload. See ParameterizedThreadStart for a code example.
Using the ParameterizedThreadStart delegate is not a type-safe way to pass data, because the Thread.Start method overload accepts any object. An alternative is to encapsulate the thread procedure and the data in a helper class and use the ThreadStart delegate to execute the thread procedure. This technique is shown in the two code examples that follow.
Neither of these delegates has a return value, because there is no place to return the data from an asynchronous call. To retrieve the results of a thread method, you can use a callback method, as demonstrated in the second code example.
using System; using System.Threading; // The ThreadWithState class contains the information needed for // a task, and the method that executes the task. // public class ThreadWithState { // State information used in the task. private string boilerplate; private int value; // The constructor obtains the state information. public ThreadWithState(string text, int number) { boilerplate = text; value = number; } // The thread procedure performs the task, such as formatting // and printing a document. public void ThreadProc() { Console.WriteLine(boilerplate, value); } } // Entry point for the example. // public class Example { public static void Main() { // Supply the state information required by the task. ThreadWithState tws = new ThreadWithState( "This report displays the number {0}.", 42); // Create a thread to execute the task, and then // start the thread. Thread t = new Thread(new ThreadStart(tws.ThreadProc)); t.Start(); Console.WriteLine("Main thread does some work, then waits."); t.Join(); Console.WriteLine( "Independent task has completed; main thread ends."); } }
Retrieving Data with Callback Methods
The following example demonstrates a callback method that retrieves data from a thread. The constructor for the class that contains the data and the thread method also accepts a delegate representing the callback method; before the thread method ends, it invokes the callback delegate.
using System; using System.Threading; // The ThreadWithState class contains the information needed for // a task, the method that executes the task, and a delegate // to call when the task is complete. // public class ThreadWithState { // State information used in the task. private string boilerplate; private int value; // Delegate used to execute the callback method when the // task is complete. private ExampleCallback callback; // The constructor obtains the state information and the // callback delegate. public ThreadWithState(string text, int number, ExampleCallback callbackDelegate) { boilerplate = text; value = number; callback = callbackDelegate; } // The thread procedure performs the task, such as // formatting and printing a document, and then invokes // the callback delegate with the number of lines printed. public void ThreadProc() { Console.WriteLine(boilerplate, value); if (callback != null) callback(1); } } // Delegate that defines the signature for the callback method. // public delegate void ExampleCallback(int lineCount); // Entry point for the example. // public class Example { public static void Main() { // Supply the state information required by the task. ThreadWithState tws = new ThreadWithState( "This report displays the number {0}.", 42, new ExampleCallback(ResultCallback) ); Thread t = new Thread(new ThreadStart(tws.ThreadProc)); t.Start(); Console.WriteLine("Main thread does some work, then waits."); t.Join(); Console.WriteLine( "Independent task has completed; main thread ends."); } // The callback method must match the signature of the // callback delegate. // public static void ResultCallback(int lineCount) { Console.WriteLine( "Independent task printed {0} lines.", lineCount); } }
"To insure that the
Main
function does not try to terminate the worker thread before it has a chance to execute, the
Main
function loops until the worker thread object's
IsAlive
property gets set to
true
:
while (!workerThread.IsAlive);"
But this link http://msdn.microsoft.com/en-us/library/ts553s52.aspx says:
"In most cases, it is not necessary to ensure that the new thread has begun executing before the thread that started it continues executing. If it is necessary, the thread that called the Start method should block ...Do not use the ThreadState and IsAlive properties to synchronize the activities of threads."
Are those at odds?
- 4/10/2012
- theredpea
In Managed Threading Basics --> Managed Thread States, (http://msdn.microsoft.com/en-us/library/d6122999.aspx) there is a note in the table for the Start method that states: "The Start method does not return until the new thread has started running. " The 3rd paragraph of this page states "The Start method returns immediately, often before the new thread has actually started."... so I assume the only way to assertain the state of a thread is to query its ThreadState property.
Thanks for pointing this out! The Managed Thread States document is correct. The third paragraph of this topic is incorrect. The error will be fixed in the next MSDN refresh.
Having said that, note that putting a thread in the Running state only means that the thread is available for scheduling by the OS. There may be a slight delay before the thread begins executing code. If it's important for the thread that calls Start to block until code in the newly started thread actually starts running, you should use a robust synchronization mechanism. For example: The calling thread blocks on a mutex as soon as it calls Start, and the newly started thread releases that mutex as its first action. This level of synchronization is not necessary in most cases.
Glenn Hackney
CLR Developer Content Team
Microsoft Corporation
- 4/28/2011
- ensconced
- 5/17/2011
- Glenn Hackney - MSFT
I tried the example but the line:
"Thread t = new Thread(new ThreadStart(tws.ThreadProc));"
had an error. I changed it to
"Thread t = Thread(new ThreadStart(tws.ThreadProc);"
and it worked?
Very odd, Ralph. Your second line should not compile at all. As written, it should generate "error CS1026: ) expected". If the missing parenthesis is supplied, it should then generate error CS0118, because System.Threading.Thread (which is a type) is being used like a method. I have no problem compiling the original code, but I get the expected errors when I use your replacement. Could you share the error message you got when you compiled the original?
-- Glenn Hackney
CLR Developer Content Team
Microsoft Corporation
- 3/7/2011
- Ralph Zegarelli
- 4/20/2011
- Glenn Hackney - MSFT
Note