The Thread Class


C#’s multithreading system is built upon the Thread class, which encapsulates a thread of execution. The Thread class is sealed, which means that it cannot be inherited. Thread defines several methods and properties that help manage threads. Throughout this chapter, several of its most commonly used members will be examined.

Creating and Starting a Thread

Prior to C# 2.0, there was only one way to create and start a thread. With the release of C# 2.0, several options were added. This section describes the basic mechanism. Later in this chapter, the new capabilities added by C# 2.0 are described.

To create a thread, you instantiate an object of type Thread. The simplest Thread constructor is shown here:

 public Thread(ThreadStart entryPoint)

Here, entryPoint is the name of the method that will be called to begin execution of the thread. ThreadStart is a delegate defined by the .NET Framework as shown here:

 public delegate void ThreadStart( )

Thus, your entry point method must have a void return type and take no arguments.

Once created, the new thread will not start running until you call its Start( ) method, which is defined by Thread. Start( ) calls the method specified by entryPoint. It has two forms. The one used here is

 public void Start( )

Once started, the thread will run until the method specified by entryPoint returns. Thus, when entryPoint returns, the thread automatically stops. If you try to call Start( ) on a thread that has already been started, a ThreadStateException will be thrown.

Remember that Thread is defined in the System.Threading namespace.

Here is an example that creates a new thread and starts it running:

 // Create a thread of execution. using System; using System.Threading; class MyThread {   public int count;   string thrdName;   public MyThread(string name) {     count = 0;     thrdName = name;   }   // Entry point of thread.   public void run() {     Console.WriteLine(thrdName + " starting.");     do {       Thread.Sleep(500);       Console.WriteLine("In " + thrdName +                         ", count is " + count);       count++;     } while(count < 10);     Console.WriteLine(thrdName + " terminating.");   } } class MultiThread {   public static void Main() {     Console.WriteLine("Main thread starting.");     // First, construct a MyThread object.     MyThread mt = new MyThread("Child #1");     // Next, construct a thread from that object.     Thread newThrd = new Thread(new ThreadStart(mt.run));     // Finally, start execution of the thread.     newThrd.Start();     do {       Console.Write(".");       Thread.Sleep(100);     } while (mt.count != 10);     Console.WriteLine("Main thread ending.");   } }

Let’s look closely at this program. MyThread defines a class that will be used to create a second thread of execution. Inside its run( ) method, a loop is established that counts from 0 to 9. Notice the call to Sleep( ), which is a static method defined by Thread. The Sleep( ) method causes the thread from which it is called to suspend execution for the specified number of milliseconds. The form used by the program is shown here:

 public static void Sleep(int milliseconds)

The number of milliseconds to suspend is specified in milliseconds. If milliseconds is zero, the calling thread is suspended only to allow a waiting thread to execute.

Inside Main( ), a new Thread object is created by the following sequence of statements:

 // First, construct a MyThread object. MyThread mt = new MyThread("Child #1"); // Next, construct a thread from that object. Thread newThrd = new Thread(new ThreadStart(mt.run)); // Finally, start execution of the thread. newThrd.Start();

As the comments suggest, first an object of MyThread is created. This object is then used to construct a Thread object by passing the mt.run( ) method as the entry point. Finally, execution of the new thread is started by calling Start( ). This causes mt.run( ) to begin executing in its own thread. After calling Start( ), execution of the main thread returns to Main( ), and it enters Main( )’s do loop. Both threads continue running, sharing the CPU, until their loops finish. The output produced by this program is as follows. (The precise output that you see may vary slightly because of differences in your execution environment, operating system, and task load.)

 Main thread starting. Child #1 starting. .....In Child #1, count is 0 .....In Child #1, count is 1 .....In Child #1, count is 2 .....In Child #1, count is 3 .....In Child #1, count is 4 .....In Child #1, count is 5 .....In Child #1, count is 6 .....In Child #1, count is 7 .....In Child #1, count is 8 .....In Child #1, count is 9 Child #1 terminating. Main thread ending.

Often in a multithreaded program, you will want the main thread to be the last thread to finish running. Technically, a program continues to run until all of its foreground threads have finished. Thus, having the main thread finish last is not a requirement. It is, however, good practice to follow, because it clearly defines your program’s endpoint. The preceding program ensures that the main thread will finish last, because the do loop stops when count equals 10. Since count will equal 10 only after newThrd has terminated, the main thread finishes last. Later in this chapter, you will see better ways for one thread to wait until another finishes.

Some Simple Improvements

While the preceding program is perfectly valid, some easy improvements will make it more efficient. First, it is possible to have a thread begin execution as soon as it is created. In the case of MyThread, this is done by instantiating a Thread object inside MyThread’s constructor. Second, there is no need for MyThread to store the name of the thread since Thread defines a property called Name that can be used for this purpose. Name is defined like this:

 public string Name { get; set; }

Since Name is a read-write property, you can use it to set the name of a thread or to retrieve the thread’s name.

Another improvement that can be made is to use the new method group conversion feature added by C# 2.0 when specifying the thread entry method. (See Chapter 15 for a discussion of method group conversions.) For the sake of illustration, the preceding program passed the thread entry method to the Thread constructor using this statement:

 Thread newThrd = new Thread(new ThreadStart(mt.run));

Here, the ThreadStart delegate’s constructor is explicitly called. However, because of method group conversion, this line can be more succinctly written like this:

 Thread newThrd = new Thread(mt.run);

In this approach, the automatic conversion from the method’s name to the delegate type is used. Because method group conversions are the modern way to handle delegates, it is the approach used by the rest of the programs in this chapter.

Here is a version of the preceding program that makes these three improvements:

 // An alternate way to start a thread. using System; using System.Threading; class MyThread {   public int count;   public Thread thrd;   public MyThread(string name) {     count = 0;     thrd = new Thread(this.run); // use method group conversion     thrd.Name = name; // set the name of the thread     thrd.Start(); // start the thread   }   // Entry point of thread.   void run() {     Console.WriteLine(thrd.Name + " starting.");     do {       Thread.Sleep(500);       Console.WriteLine("In " + thrd.Name +                         ", count is " + count);       count++;     } while(count < 10);     Console.WriteLine(thrd.Name + " terminating.");   } } class MultiThreadImproved {   public static void Main() {     Console.WriteLine("Main thread starting.");     // First, construct a MyThread object.     MyThread mt = new MyThread("Child #1");     do {       Console.Write(".");       Thread.Sleep(100);     } while (mt.count != 10);     Console.WriteLine("Main thread ending.");   } }

This version produces the same output as before. Notice that the thread object is stored in thrd inside MyThread.

Creating Multiple Threads

The preceding examples have created only one child thread. However, your program can spawn as many threads as it needs. For example, the following program creates three child threads:

 // Create multiple threads of execution. using System; using System.Threading; class MyThread {   public int count;   public Thread thrd;   public MyThread(string name) {     count = 0;     thrd = new Thread(this.run);     thrd.Name = name;     thrd.Start();   }   // Entry point of thread.   void run() {     Console.WriteLine(thrd.Name + " starting.");     do {       Thread.Sleep(500);       Console.WriteLine("In " + thrd.Name +                         ", count is " + count);       count++;     } while(count < 10);     Console.WriteLine(thrd.Name + " terminating.");   } } class MoreThreads {   public static void Main() {     Console.WriteLine("Main thread starting.");     // Construct three threads.     MyThread mt1 = new MyThread("Child #1");     MyThread mt2 = new MyThread("Child #2");     MyThread mt3 = new MyThread("Child #3");     do {       Console.Write(".");       Thread.Sleep(100);     } while (mt1.count < 10 ||              mt2.count < 10 ||              mt3.count < 10);     Console.WriteLine("Main thread ending.");   } }

Sample output from this program is shown next:

 Main thread starting. .Child #1 starting. Child #2 starting. Child #3 starting. ....In Child #1, count is 0 In Child #2, count is 0 In Child #3, count is 0 .....In Child #1, count is 1 In Child #2, count is 1 In Child #3, count is 1 .....In Child #1, count is 2 In Child #2, count is 2 In Child #3, count is 2 .....In Child #1, count is 3 In Child #2, count is 3 In Child #3, count is 3 .....In Child #1, count is 4 In Child #2, count is 4 In Child #3, count is 4 .....In Child #1, count is 5 In Child #2, count is 5 In Child #3, count is 5 .....In Child #1, count is 6 In Child #2, count is 6 In Child #3, count is 6 .....In Child #1, count is 7 In Child #2, count is 7 In Child #3, count is 7 .....In Child #1, count is 8 In Child #2, count is 8 In Child #3, count is 8 .....In Child #1, count is 9 Child #1 terminating. In Child #2, count is 9 Child #2 terminating. In Child #3, count is 9 Child #3 terminating. Main thread ending.

As you can see, once started, all three child threads share the CPU. Because of differences between system configurations, operating systems, and other environmental factors, when you run the program, the output that you see may differ slightly from that shown here.




C# 2.0(c) The Complete Reference
C# 2.0: The Complete Reference (Complete Reference Series)
ISBN: 0072262095
EAN: 2147483647
Year: 2006
Pages: 300

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net