FREE BOOK

Chapter 10: Processes, AppDomains,Contexts, and Threads

Posted by Apress Free Book | C#.NET February 02, 2009
In this Chapter you drill deeper into the constitution of a .NET executable host and come to understand the relationship between Win32 processes, application domains, contexts, and threads.

The VS .NET Threads Window

To wrap up our initial investigation of threads, it is worth pointing out that the Visual Studio .NET IDE provides a Threads window, which can be accessed from the Debug | Windows menu item during a debugging session. As you can see from Figure 10-12, this window allows you to view the set of currently executing threads in your .NET assembly.



Figure 10-12. The VS .NET Threads window

SOURCE CODE The ThreadStats project is included under the Chapter 10 subdirectory.

A More Elaborate Threading Example

Now that you have seen the basic process of creating a new thread of execution, we can turn to a more illustrative example. Create a new console application named SimpleMultiThreadApp. Next, define a helper class that supports a public method named DoSomeWork():

    internal class WorkerClass
    {        public void DoSomeWork()
        {
            // Get hash code for this worker thread.
            Console.WriteLine("ID of worker thread is: {0} ",
            Thread.CurrentThread.GetHashCode());
            // Do the work.
            Console.Write("Worker says: ");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i + ", ");
            }
            Console.WriteLine();
        }
    }

Now assume the Main() method creates a new instance of WorkerClass. For the primary thread to continue processing its workflow, create and start a new Thread that is configured to execute the DoSomeWork() method of the WorkerClass type:

   public class MainClass
    {
        public static int Main(string[] args)
        {
            // Get hash code of the current thread.
            Console.WriteLine("ID of primary thread is: {0} ",
            Thread.CurrentThread.GetHashCode());
            // Make worker class.
            WorkerClass w = new WorkerClass();
            // Now make (and start) the worker thread.
            Thread workerThread =
            new Thread(new ThreadStart(w.DoSomeWork));
            workerThread.Start();
            return 0;
        }
    }

If you run the application you would find each thread has a unique hash code (which is a good thing, as you should have two separate threads at this point).

Clogging Up the Primary Thread

Currently, our application creates a secondary thread to perform a unit of work (in this case, printing 10 numbers). The problem is the fact that printing 10 numbers takes no time at all, and therefore we are not really able to appreciate the fact that the primary thread is free to continue processing. Let's update the application to illustrate this very fact. First, let's tweak the WorkerClass to print out 30,000 numbers, to account for a more labor-intensive process:

    internal class WorkerClass
    {
        public void DoSomeWork()
        { 
            // Do a lot of work.
            Console.Write("Worker says: ");
            for (int i = 0; i < 30000; i++)
            { Console.WriteLine(i + ", "); }
            Console.WriteLine();
        }
    }

Next, update the MainClass such that it launches a Windows Forms message box directly after it creates the worker thread (don't forget to set a reference to System.Windows.Forms.dll):

    public class MainClass
    {
        public static int Main(string[] args)
        {
            // Create worker thread as before.
            // Now while worker thread is busy,
            // do some additional work on primary thread.
            MessageBox.Show("I'm buzy");
            return 0;
        }
    }

If you were to now run the application, you would see that the message box is displayed and can be moved around the desktop while the worker thread is busy pumping numbers to the console (Figure 10-13).



Figure 10-13. Two threads each performing a unit of work

Now, contrast this behavior with what you might find if you had a single-threaded application. Assume the Main() method has been updated with logic that allows the user to enter the number of threads used within the current AppDomain (one or two):

       public static int Main(string[] args)
        {
            Console.Write("Do you want [1] or [2] threads? ");
            string threadCount = Console.ReadLine();
            // Make worker class.
            WorkerClass w = new WorkerClass();
            // Only make a new thread if the user said so.
            if (threadCount == "2")
            {
                // Now make the thread.
                Thread workerThread =
                new Thread(new ThreadStart(w.DoSomeWork));
                workerThread.Start();
            }
            else // Execute this method on the single thread.
                w.DoSomeWork();
            // Do some additional work.
            MessageBox.Show("I'm buzy");
            return 0;
        }

As you can guess, if the user enters the value "1" he or she must wait for all 30,000 numbers to be printed before seeing the message box appear, given that there is only a single thread in the AppDomain. However, if the user enters "2" he or she is able to interact with the message box while the secondary thread spins right along.

Putting a Thread to Sleep

The static Thread.Sleep() method can be used to currently suspend the current thread for a specified amount of time (specified in milliseconds). In particular, you can use this to pause a program. To illustrate, let's update the WorkerClass again. This time around, the DoSomeWork() method does not print out 30,000 lines to the console, but 10 lines. The trick is, between each call to Console.WriteLine(), this worker thread is put to sleep for approximately 2 seconds.

   internal class WorkerClass
    {
        public void DoSomeWork()
        {
            // Get some information about the worker thread.
            Console.WriteLine("ID of worker thread is: { 0} ",
            Thread.CurrentThread.GetHashCode());
            // Do the work (and take a nap).
            Console.Write("Worker says: ");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(i + ", ");
                Thread.Sleep(2000);
            }
            Console.WriteLine();
        }
    }

Now run your application a few times and specify both threading options. You will find radically different behaviors based on your choice of thread number.

SOURCE CODE The SimpleMultiThreadApp project is included under the Chapter 10 subdirectory.

Total Pages : 13 910111213

comments