process that creates two or more threads is called a multithreaded process. ... In C#, you create a thread by creating a
A program executes as a single, sequential flow of control. A program can also be designed to execute multiple tasks. To execute multiple tasks in your program, you can use threads. This chapter discusses implementation of threads and their life cycle. The chapter also discusses implementing multiple threads and prioritizing threads. In addition, this chapter discusses thread synchronization and communication between processes.
Objectives In this chapter, you will learn to: Implement threads Define the life cycle of a thread Implement multiple threads Identify the thread priority Use synchronization in threads Identify communication between processes
Chapter 11
Creating Multithreaded Applications
Implementing Threads A thread is defined as the execution path of a program. You can define a unique flow of a control in a program, using a thread. Threads are used to run applications that perform large and complex computations. For example, a Central Processing Unit (CPU) performs various complex tasks simultaneously. The processes include tasks such as writing and printing a document, installing software, and displaying the date and time on the status bar. All these processes are handled by separate threads. A process that is executed using one thread is known as a single-threaded process, where the process is a running instance of a program. A single-threaded application can perform only one task at a time. You have to wait for one task to complete before another task can start. The following figure shows a single-threaded process.
A Single-Threaded Process
To execute more than one task at a time, you can create multiple threads in a program. A process that creates two or more threads is called a multithreaded process. For example, any Web browser, such as Internet Explorer is a multithreaded application. Within the browser, you can print a page in the background while you scroll the page. Similarly, you can play audio files and watch animated images at the same time. Each thread in a multithreaded process runs at the same time and maintains a different execution flow. The following figure shows a multithreaded process with two threads.
A Multithreaded Process
¤NIIT
Creating Multithreaded Applications 11.3
The Thread Model A program uses threads to increase the efficiency of a CPU by preventing wastage of the CPU cycles. In single-threaded systems, an approach called event loop with polling is used. Polling is the process in which a single event is executed at a time. In the event loop with polling approach, a single thread runs in an infinite loop till its operation is completed. When the operation is completed, the event loop dispatches control to the appropriate event-handler. No more processing can happen in the system until the event-handler returns. This results in the wastage of the CPU time. In a single-threaded application if the thread is suspended from execution because it is waiting for a system resource, the entire program stops executing. This limitation can be overcome by using multithreading, which eliminates the event loop with polling approach. In multithreading, the time for which a thread waits for the CPU time can be utilized to perform another task. In C#, you will use the Thread class to work with threads. The System.Threading.Thread class is used to construct and access individual threads in a multithreaded application. The first thread to be executed in a process is called the main thread.
The Main Thread The main thread is created automatically on the start up of a C# program execution. The threads which are created exclusively using the Thread class are called as child threads, where the main thread is either called a parent thread or a primary thread. You can access a thread using the CurrentThread property of the Thread class. The following code shows the execution of main thread using its CurrentThread property in the Thread class: using System; using System.Threading; namespace ThreadExample { class MainThreadExample { public static void Main(string[] args) { Thread Th = Thread.CurrentThread; Th.Name = "MainThread"; Console.WriteLine("The current thread after name change :{0}", Th.Name); Console.ReadLine(); 11.4 Creating Multithreaded Applications
¤NIIT
}
}
}
The output of the preceding code is as follows.
Output of the Main Thread Program
In the preceding code, a reference to the current thread is obtained by using the CurrentThread parameter of the Thread class and its reference is stored in the Th variable. The Name parameter is used to set the name of the thread, and the information about the thread is displayed.
Working with Threads In C#, you create a thread by creating an object of type Thread, giving its constructor a and calling the new thread’s Start() method. The new thread starts executing asynchronously with an invocation of the thread’s method. When the method returns, the thread dies. ThreadStart reference,
The other methods of the Thread class allow managing the lifetime of the thread and destroying the thread when required. There are various methods available with the Thread class. Using these methods, you can control the execution of threads. Few of these methods are: Start(): Starts a thread Sleep(): Makes the thread to pause for a period of time ¤NIIT
Creating Multithreaded Applications 11.5
Abort():
Terminates the thread Suspend(): Suspends a thread. If the thread is already suspended it has no effect Resume(): Resumes the suspended thread
Working with threads involves creating, managing, and destroying threads.
Creating Threads You can create threads by extending the Thread class. The extended Thread class calls the Start() method to begin the child thread execution. You can use the following code to create a thread by extending the Thread class: using System; using System.Threading; namespace ThreadSample { class BasicThreadApp { public static void ChildThreadCall() { Console.WriteLine("Child thread started"); } public static void Main() { ThreadStart ChildRef = new ThreadStart(ChildThreadCall); Console.WriteLine("Main - Creating Child thread"); Thread ChildThread = new Thread(ChildRef); ChildThread.Start();
} }
Console.WriteLine("Main - Have requested the start of child thread"); Console.ReadLine();
}
11.6 Creating Multithreaded Applications
¤NIIT
The output of the preceding code is as follows.
Output of the Create Thread Program
In the preceding output, the Main() method prints before the message from the child thread, proving that the child thread is indeed working asynchronously. The first statement in the preceding code is the using statement for the System.Threading namespace. The first statement of the Main() method specifies the method to be invoked by the thread: ThreadStart ChileRef = new ThreadStart(ChildThreadCall);
The next statement instantiates a Thread object where the constructor takes the object of the ThreadStart class as its argument: Thread ChildThreads = new Thread(ChildRef);
Then, the Start() method of the Thread object is called, and the method results in a call to the ChildThreadCall method. The extended Thread class calls the Start() method to begin the child thread execution.
Managing Threads There are many tasks you might need to perform to manage the activity or life of a thread. You can manage all these tasks by using the various thread methods available with the Thread class. ¤NIIT
Creating Multithreaded Applications 11.7
For example, when you require the thread to pause for a period of time so that the thread is allowed to execute, you can call the Thread.Sleep() method. This method takes a single argument that represents time in milliseconds for which you want the thread to pause. This method is a static method and cannot be called with an instance of a thread object. This is done to avoid a call to the Thread.Sleep() method on any other thread except the currently executing method. The static Thread.Sleep() method calls the static CurrentThread method, which then pauses that thread for the specified amount of time. The following code shows the implementation of the Sleep() method: using System; using System.Threading; namespace ThreadSample { class BasicThreadApp { public static void ChildThreadCall() { Console.WriteLine("Child thread started"); int SleepTime = 5000; Console.WriteLine("Sleeping for {0} seconds", SleepTime/1000); Thread.Sleep(SleepTime); //Sleep for five seconds. Console.WriteLine("Waking Up"); } public static void Main() { ThreadStart ChildRef = new ThreadStart(ChildThreadCall); Console.WriteLine("Main - Creating Child thread"); Thread ChildThread = new Thread(ChildRef); ChildThread.Start(); Console.WriteLine ("Main - Have requested the start of child thread"); Console.ReadLine(); }
}
}
11.8 Creating Multithreaded Applications
¤NIIT
The output of the preceding code is as follows.
Output of the Managing Threads Program
There is more than one way to call the Sleep() method. One way is to call Thread.Sleep() with a value 0, which will cause the current thread to hand over the unused balance of its timeslice. The other way is to call Thread.sleep() method with a value of Timeout.Infinite, which results in the thread being paused indefinitely until it is interrupted by another thread calling the suspended thread’s Thread.Interrupt() method. There is another method Thread.Suspend(), which is used to suspend the execution of a thread. The Thread.Suspend() method can be called on the currently executing thread or another thread. When a thread is suspended in this fashion, only another thread can cause its resumption, with the Thread.Resume() method. Notice, when a thread suspends another thread, the first thread is not blocked. The call returns immediately. In addition, regardless of how many times the Thread.Suspend() method is called for a given thread, a single call to Thread.Resume() will cause the thread to resume execution.
Destroying Threads If the thread is required to be destroyed, the Thread.Abort() method will allow you to accomplish the task. The runtime aborts the thread by throwing a ThreadAbortException. This exception cannot be caught. If the finally block is present in the method, the runtime will send the control to it. ¤NIIT
Creating Multithreaded Applications 11.9
You can use the following code to destroy threads: using System; using System.Threading; namespace ThreadSample { class BasicThreadApp { public static void ChildThreadCall() { try { Console.WriteLine("Child thread started"); Console.WriteLine ("Child thread - counting to 10"); for (int i = 0; i < 10; i++) { Thread.Sleep(500); Console.Write("{0}...", i); } Console.WriteLine("Child thread finished"); } catch (ThreadAbortException e) { Console.WriteLine("Exception"); } finally { Console.WriteLine ("Child thread -Unable to catch the exception."); } } public static void Main() { ThreadStart ChildRef = new ThreadStart(ChildThreadCall); Console.WriteLine("Main - Creating Child thread"); Thread ChildThread = new Thread(ChildRef); ChildThread.Start(); //Give the Child thread time to start. Console.WriteLine("Main - Sleeping for 2 seconds"); Thread.Sleep(2000);
} }
Console.WriteLine("\nMain - Aborting Child thread"); ChildThread.Abort(); Console.ReadLine();
}
11.10 Creating Multithreaded Applications
¤NIIT
The output of the preceding code is as follows.
Output of the Destroy Thread Program
In the preceding code, the Main() method pauses for two seconds to make sure that the runtime has had time to start the worker thread. When started, the worker thread starts counting to ten, pausing for a second in between each number. When the Main() method resumes execution after its two seconds pause, it aborts the worker thread. Notice, the finally block is executed after the abort.
¤NIIT
Creating Multithreaded Applications 11.11
Implementing Thread Life Cycle The life cycle of a thread starts when an object of the System.Threading.Thread class is created. The life cycle of the thread ends with task execution. There are various states in the life cycle of a thread. These states are: The Unstarted state The Runnable state The Not Runnable state The Dead state The following figure shows the various thread states and methods, which can cause the thread to move from one state to another.
Life Cycle of a Thread
The Unstarted State When an instance of the Thread class is created, the thread enters the unstarted state. A new thread is an empty object of the Thread class, and no system resources such as memory are allocated to it. You have to invoke the Start() method to start the thread.
11.12 Creating Multithreaded Applications
¤NIIT
The Runnable State The thread remains in the unstarted state until the program calls the Start() method of the Thread class, which places the thread in the runnable state and immediately returns control to the calling thread. This state is also called as the ready or started state. The newly started thread and any other threads in the program execute concurrently. A single processor cannot execute more than one thread at a time. Therefore, it maintains a thread queue. When a thread is started, a thread is queued up for the processor time and waits for its turn to get executed. As a result, the state of the thread is said to be runnable and not running.
The Not Runnable State A thread is not in the runnable state if it is: Sleeping: A thread is put into the sleeping mode by calling the Sleep() method. A sleeping thread enters the runnable state after the specified time of sleep has elapsed. Waiting: A thread can be made to wait for some specified condition to be satisfied by calling the Wait() method. The thread can be notified of the condition by invoking the Pulse() method of the Thread class. Blocked: A thread could be blocked by an I/O operation.When the thread is blocked, it enters the not runnable state.
The Dead State A running thread enters the dead state when the statements of the threads method are complete. This state is also called the terminated state. A program can force a thread into the dead state by calling the Abort() method of the Thread class on the appropriate thread object. The Abort() method throws a ThreadAbortException in the thread, normally causing the thread to terminate. When a thread is in the dead state and there are no references to the thread object, the garbage collector can remove the thread object from memory.
¤NIIT
Creating Multithreaded Applications 11.13
Activity: Hangman Game Problem Statement The next door children request you to create the Hangman game for them. The game asks a user to enter a category as Book or Movie. Based on the category, a book name or movie name is extracted and the user is asked to guess the name by giving the character and its position in a string. A user will get 60 seconds to play the game. Develop the Hangman game application.
Solution To solve the preceding problem, you need to perform the following tasks: 1. Identify the data source where the name of the book or movie is stored. 2. Create a console-based application for the Hangman game. 3. Build and execute the application. Task 1: Identifying the Data Source Where the Name of the Book or Movie is Stored To demonstrate the implementation of the Hangman game, you need to perform the following steps: 1. Identify the data source. The data source from where data needs to be retrieved is TextTest.txt. 2. Save the TextTest.txt in C drive. Task 2: Creating a Console-Based Application for the Hangman Game To create a console-based application for the Hangman game, you need to perform the following steps: 1. Select StartÆAll ProgramsÆMicrosoft Visual Studio 2005ÆMicrosoft Visual Studio 2005. The Start Page - Microsoft Visual Studio 2005 window is displayed. 2. Select FileÆNewÆProject. The New Project window is displayed. 3. Select the Project type as Visual C# from the Project types pane and Console Application from the Templates pane. 4. Type the name of the new project as HangmanApp in the Name text box. 5. Specify the location where the new project is to be created as c:\chapter11\Activity1 in the Location combo box. 6. Click the OK button. 11.14 Creating Multithreaded Applications
¤NIIT
7.
Open Solution Explorer window and right-click the Program.cs file. The shortcut menu is displayed. 8. Select the Rename option and type the name as Hangman.cs. 9. Double-click the Hangman.cs file in the Solution Explorer window. The code view of Hangman.cs class is displayed. 10. Replace the existing code with the following code: /*Program to play Hangman game. The program asks a user to enter a category as Book/Movie. Based on the category provided, a book name or category name is extracted and the user is asked to guess the name by giving the character and its position.*/ using System; using System.IO; using System.Threading; namespace Game { public class Hangman { string randomString, userString; int dataLength; string Category; string[] bookData = new string[5]; string[] movieData = new string[5]; int bookCount = 0, movieCount = 0; public Hangman() { FillNameValues(); } // Stores the movie names and Book names in respective strings private void FillNameValues() { //Declaring the variables string firstLine; // open the file for reading StreamReader sRead = new StreamReader("c:\\TextTest.txt"); sRead.BaseStream.Seek(0, SeekOrigin.Begin); //Reading the content of the file firstLine = sRead.ReadLine(); while (firstLine != null) { //Storing the Book names in the BookData array if (firstLine.Substring(0, 1) == "B") { int stringStartPos = firstLine.IndexOf(':'); bookData[bookCount] = firstLine.Substring(stringStartPos + 1); bookCount++; ¤NIIT
Creating Multithreaded Applications 11.15
} //Storing the Movie names in the MovieData array else { int stringStartPos = firstLine.IndexOf(':'); movieData[movieCount] = firstLine.Substring(stringStartPos + 1); movieCount++; } firstLine = sRead.ReadLine(); } } public int AcceptCategory() { //Accepting the choice of the user in terms of the Category Console.WriteLine("Enter the Category to Play Book/Movie"); Category = Console.ReadLine(); Category = Category.ToUpper(); if (Category != "BOOK" && Category != "MOVIE") { Console.WriteLine("Invalid Category....\n"); return 0; } else { ExtractName(); return 1; } } public void ExtractName() { //Creating the object of the Random class Random RandGen = new Random(); if (Category == "BOOK") {//Randomly selecting the Book name from the array int Rnd = RandGen.Next(0, bookCount - 1); //Calling the play method randomString = bookData[Rnd]; } else {//Randomly selecting the Movie name from the array int Rnd = RandGen.Next(0, movieCount - 1); //Calling the Startplay method randomString = movieData[Rnd]; } } /*This method will allow the user to give the characters and 11.16 Creating Multithreaded Applications
¤NIIT
displaying his status as to WON or LOST */ public void StartGame() { //Calculating the length of movie/book name dataLength = randomString.Length; //Declaring Variables char locateChar; int correctCnt = 0, inCorrectCnt = 0; int i, k; //Decalring string to store user input char[] s = new char[randomString.Length]; //Loop to accept the characters and its positions //Loop allows user to attempt 2 times more than the total characters InitializeUserString(); ShowUserInputString(); if (Category == "BOOK") { Console.WriteLine("The total number of characters in the Book: {0}", randomString.Length); Console.WriteLine("The total number of characters you can enter to guess the name of Book: {0}", randomString.Length + 2); } else { Console.WriteLine("The total number of characters in the Movie: {0}", randomString.Length); Console.WriteLine("The total number of characters you can enter to guess the name of Movie: {0}", randomString.Length + 2); } for (i = 1, k = 0; i