At any time, a thread is said to be in one of several thread states that are illustrated in the UML state diagram of Fig. 15.1. This section discusses these states and the transitions between states. Two classes critical for multithreaded applications are Thread and Monitor (System.Threading namespace). This section also discusses several methods of classes THRead and Monitor that cause state transitions. A few of the terms in the diagram are discussed in later sections.
Figure 15.1. Thread life cycle.
(This item is displayed on page 722 in the print version)
A Thread object begins its life cycle in the Unstarted state when the program creates the object and passes a ThreadStart delegate to the object's constructor. The THReadStart delegate, which specifies the actions the thread will perform during its life cycle, must be initialized with a method that returns void and takes no arguments. [Note: .NET 2.0 also includes a ParameterizedThreadStart delegate to which you can pass a method that takes arguments. For more information, visit the site msdn2.microsoft.com/en-us/library/xzehzsds.] The thread remains in the Unstarted state until the program calls the Thread's Start method, which places the thread in the Running state and immediately returns control to the part of the program that called Start. Then the newly Running thread and any other threads in the program can execute concurrently on a multiprocessor system or share the processor on a system with a single processor.
While in the Running state, the thread may not actually be executing all the time. The thread executes in the Running state only when the operating system assigns a processor to the thread. When a Running thread receives a processor for the first time, the thread begins executing the method specified by its ThreadStart delegate.
A Running thread enters the Stopped (or Aborted) state when its THReadStart delegate terminates, which normally indicates that the thread has completed its task. Note that a program can force a thread into the Stopped state by calling Thread method Abort on the appropriate THRead object. Method Abort tHRows a ThreadAbortException in the thread, normally causing the thread to terminate. When a thread is in the Stopped state and there are no references to the thread object, the garbage collector can remove the thread object from memory. [Note: Internally, when a thread's Abort method is called, the thread actually enters the AbortRequested state before entering the Stopped state. The thread remains in the AbortRequested state while waiting to receive the pending ThreadAbortException. When Abort is called, if the thread is in the WaitSleepJoin, Suspended or Blocked state, the thread resides in its current state and the AbortRequested state, and cannot receive the ThreadAbortException until it leaves its current state.]
A thread is considered Blocked if it is unable to use a processor even if one is available. For example, a thread becomes blocked when it issues an input/output (I/O) request. The operating system blocks the thread from executing until the operating system can complete the I/O request for which the thread is waiting. At that point, the thread returns to the Running state, so it can resume execution. Another case in which a thread becomes blocked is in thread synchronization (Section 15.5). A thread being synchronized must acquire a lock on an object by calling Monitor method Enter. If a lock is not available, the thread is blocked until the desired lock becomes available. [Note: The Blocked state is not an actual state in .NET. It is a conceptual state that describes a thread that is not Running.]
There are three ways in which a Running thread enters the WaitSleepJoin state. If a thread encounters code that it cannot execute yet (normally because a condition is not satisfied), the thread can call Monitor method Wait to enter the WaitSleepJoin state. Once in this state, a thread returns to the Running state when another thread invokes Monitor method Pulse or PulseAll. Method Pulse moves the next waiting thread back to the Running state. Method PulseAll moves all waiting threads back to the Running state.
A Running thread can call Thread method Sleep to enter the WaitSleepJoin state for a period of milliseconds specified as the argument to Sleep. A sleeping thread returns to the Running state when its designated sleep time expires. Sleeping threads cannot use a processor, even if one is available.
Any thread that enters the WaitSleepJoin state by calling Monitor method Wait or by calling Thread method Sleep also leaves the WaitSleepJoin state and returns to the Running state if the sleeping or waiting THRead's Interrupt method is called by another thread in the program. The Interrupt method causes a THReadInterruptionException to be thrown in the interrupted thread.
If a thread cannot continue executing (we will call this the dependent thread) unless another thread terminates, the dependent thread calls the other thread's Join method to "join" the two threads. When two threads are "joined," the dependent thread leaves the WaitSleepJoin state and re-enters the Running state when the other thread finishes execution (enters the Stopped state).
If a Running THRead's Suspend method is called, the Running thread enters the Suspended state. A Suspended thread returns to the Running state when another thread in the program invokes the Suspended thread's Resume method. [Note: Internally, when a thread's Suspend method is called, the thread actually enters the SuspendRequested state before entering the Suspended state. The thread remains in the SuspendRequested state while waiting to respond to the Suspend request. If the thread is in the WaitSleepJoin state or is blocked when its Suspend method is called, the thread resides in its current state and the SuspendRequested state, and cannot respond to the Suspend request until it leaves its current state.] Methods Suspend and Resume are now deprecated and should not be used. In Section 15.9, we show how to emulate these methods using thread synchronization.
If a thread's IsBackground property is set to true, the thread resides in the Background state (not shown in Fig. 15.1). A thread can reside in the Background state and any other state simultaneously. A process must wait for all foreground threads (threads not in the Background state) to finish executing and enter the Stopped state before the process can terminate. However, if the only threads remaining in a process are Background threads, the CLR terminates each thread by invoking its Abort method, and the process terminates.
Introduction to Computers, the Internet and Visual C#
Introduction to the Visual C# 2005 Express Edition IDE
Introduction to C# Applications
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Methods: A Deeper Look
Classes and Objects: A Deeper Look
Object-Oriented Programming: Inheritance
Polymorphism, Interfaces & Operator Overloading
Graphical User Interface Concepts: Part 1
Graphical User Interface Concepts: Part 2
Strings, Characters and Regular Expressions
Graphics and Multimedia
Files and Streams
Extensible Markup Language (XML)
Database, SQL and ADO.NET
ASP.NET 2.0, Web Forms and Web Controls
Networking: Streams-Based Sockets and Datagrams
Searching and Sorting
Appendix A. Operator Precedence Chart
Appendix B. Number Systems
Appendix C. Using the Visual Studio 2005 Debugger
Appendix D. ASCII Character Set
Appendix E. Unicode®
Appendix F. Introduction to XHTML: Part 1
Appendix G. Introduction to XHTML: Part 2
Appendix H. HTML/XHTML Special Characters
Appendix I. HTML/XHTML Colors
Appendix J. ATM Case Study Code
Appendix K. UML 2: Additional Diagram Types
Appendix L. Simple Types