Multithreading Part 2: Understanding the System.Threading.Thread Class
In this article we will study the .NET threading API, how to create threads in C#, start and stop them, define their priorities and states.
Applications written on the .NET platform are naturally threaded. Let us study threading with respect to C# as the programming language. The runtime environment starts execution of the program with the Main () method in one thread. We all know that there is an automatic garbage collection happening in the background. This takes place in another thread. All of this happens so naturally that it goes unnoticed. There are situations when we need to add threads to our application. We have already seen the numerous situations in Multithreading — part1 of the article, where we might feel the need to add new threads. A few common requirements being when you need to handle user-input, or do a lengthy calculation etc. To put it more generally, any operation that is time-critical, or needs constant attention or is time-consuming should be placed in a thread of its own.
The classes and interfaces in the System.Threading namespace provide the multithreading support in the .NET platform. This namespace consists of a number of classes. We will be discussing the Thread class of this namespace.
System.Threading.Thread is the main class for creating threads and controlling them. The Thread class has a number of methods. A few interesting methods are shown below:
- Start(): starts the execution of the thread.
- Suspend(): suspends the thread, if the thread is already suspended, nothing happens.
- Resume() : resumes a thread that has been suspended.
- Interrupt(): interrupts a thread that is in the wait, sleep or join stage.
- Join(): blocks a calling thread until the thread terminates.
- Sleep(int x) : suspends the thread for specified amount of time (in milliseconds).
- Abort(): Begins the process of terminating the thread. Once the thread terminates, it cannot be restarted by calling the function Start() again.
You can pause/block a thread by calling Thread.Sleep or Thread.Suspend or Thread.Join. Calling the method Sleep() or Suspend() on a thread means, the thread does not get any processor time. There is a difference between these two ways of pausing a thread. Thread.Sleep causes a thread to stop immediately but the common language runtime waits until the thread has reached some safe point before calling the Suspend() method on the thread. One thread cannot call Sleep() on another thread but one thread can call Suspend() on the other thread and it causes the other thread to pause. Calling Resume() on the suspended thread breaks the thread out of the suspended state and allows it to continue execution. A single call to Resume() is sufficient to activate a thread regardless of the number of times Suspend() was called to block it. A thread that has already been terminated or has not yet started functioning cannot be suspended. Thread.Sleep(int.Infinite) causes a thread to sleep indefinitely. The thread can only wake up when it is interrupted by another thread that calls Thread.Interrupt or is aborted by Thread.Abort. You can use Thread.Interrupt to break a thread out of its blocking state but it throws a ThreadInterupptedException. You can either catch the exception, do whatever you want to do with the thread, or ignore the exception and let the run-time stop the thread. For managed wait, Thread.Interrupt and Thread.Abort both wake up the thread immediately.
It may be desirable at times to terminate a thread from some other thread. In such situations you use the Thread.Abort method to stop a thread permanently and using this function throws a ThreadAbortException. The terminating thread can catch the exception but it is difficult to suppress it. The only way that it can be suppressed is by calling Thread.ResetAbort method but it can only be called if this thread had been the one that had provoked the exception. Since, Thread.Abort is normally called by some thread A on some other thread B, B therefore, cannot invoke the method Thread.ResetAbort to suppress it from terminating. The Thread.Abort method lets the system quietly stop the thread without informing the user. Once aborted, a thread cannot be restarted. As this method does not say that the thread will abort immediately, hence to be sure that the thread has terminated, you can call Thread.Join to wait on the thread. Join is a blocking call that does not return until the thread has actually stopped executing. But remember, a thread may call Thread.Interrupt to interrupt another thread that is waiting on a call to Thread.Join.
As far as possible, you should avoid using Suspend() to block a thread as it could lead to serious problems like deadlocks. Imagine, what would happen if we suspend a thread that holds a resource that another thread would need. Rather, you should give different priorities to the thread based on their importance. You should, as far as possible, use Thread.Priority rather than Thread.Suspend.
This class also has a number of interesting properties as shown below:
- IsAlive: (if true, signifies that thread has been started and has not yet been terminated or aborted)
- Name (gets/sets the name of the thread)
- Priority (gets/sets the scheduling priority of a thread)
- ThreadState (gets a value containing the state of the current thread).