The series so far:
- The Unconventional Guide to Introduction to Thread
- Why Do People Think Dedicated CLR Threads is a Good Idea?
- How Not Knowing Thread Members and Execution State Makes You a Rookie
- Doing Thread Scheduling and Priority the Right Way
- How to Start Using CLR's Thread Pool
- What Wikipedia Can't Tell You About Thread Execution Contexts
- The Insider's Guide to Cooperative Cancellation and Timeout
Execution States of a thread
ThreadState
Enumeration
Thread State Cycle
Common States
The most common states as used in the block diagram:
Unstarted
:
A thread is Created within the common language runtime but not Started still.Running
:
After a Thread calls Start methodWaitSleepJoin
:
After a Thread calls its Wait or Sleep or Join methodSuspended
:
Thread Responds to a Suspend method call.Stopped
:
The Thread is Stopped, either normally or Aborted.
Other States
Apart from these states that we have already seen in the block diagram, other states are:
SuspendRequested
:
The thread is being requested to suspend.AbortRequested
:
After theThread.Abort
method invokes on the thread, but the thread has not yet received the pendingThreadAbortException
that attempts to terminate it.Aborted
:
The thread state includesAbortRequested
and the thread is now dead, but its state has not yet changed to Stopped.StopRequested
:
This state is for internal use only, when thread blocks on a call toWait
, and another thread callsAbort
on the blocked thread, the blocked thread state is in both theWaitSleepJoin
and theAbortRequested
states at the same time. In this case, as soon as the thread returns from the call toWait
or is interrupted, it receives theThreadAbortException
to begin aborting.
Members of Thread Class
Thread
class creates and controls a thread, sets its priority, and gets its status.
ThreadState
:
Specifies the execution states of a Thread.Thread (ThreadStart)
:
Initializes a new instance of the Thread class andThreadStart
represents the method that executes on a Thread.void Start ()
Causes the operating system to change the state of the current instance toThreadState.Running
, and optionally supplies an object containing data to be used by the method the thread executes.static bool Yield()
Causes the calling thread toyield
execution to another thread that is ready to run on the current processor. (the operating system selects the thread to yield to)Name
Gets or sets the name of the threadIsAlive
Gets a value indicating the execution status of the current threadIsBackground
Gets or sets a value indicating whether or not a thread is a background thread. We cover in detail in the next topic.static CurrentThread
Retrieve a reference to the currently executing thread from the code that the thread is executing.static Sleep (int millisecondsTimeout)
Suspends the current thread for the specified number of millisecondsstatic Sleep(TimeSpan time)
Suspends the current thread for the specified amount of time.public void Interrupt()
Interrupts a thread that is in the WaitSleepJoin thread state. If this thread is not currently blocked in a wait, sleep, or join state, it will be interrupted when it next begins to block. This method causes theThreadInterruptedException
to be thrown when the thread is blocked.public void Abort()
Called on an thread that is under execution which causes the thread to stop and throwsThreadAbortException
.
Important Notes on ThreadAbortException and ResetAbort
- The thread is not guaranteed to abort immediately, or at all. This situation may arise as a result of lot of computation in the
finally
blocks that are called as part of the abort whenThreadAbortException
is thrown.ThreadAbortException
is a special exception that can be caught by application code, but is re-thrown at the end of thecatch
block unlessResetAbort
is called.ResetAbort
cancels the request to abort, and prevents theThreadAbortException
from terminating the thread.
Caution Note!
Do not use theSuspend
andResume
methods to synchronise the activities of threads.
- You have no way of knowing what code a thread is executing when you suspend it.
- If you suspend a thread while it holds locks during a security permission evaluation, it might block other threads in the AppDomain.
- If you suspend a thread while it is executing a class constructor, other threads get blocked in the AppDomain that attempt to use that class. Deadlocks can occur very quickly.
DEMO
1. Passing Parameter to threads with ThreadStart
The ThreadStart
delegate
doesn't take any parameters. We generally create a new instance of a class and use its instance to store the information we need to pass down.
// Define a class to store information
public class FileParser
{
string path;
public FileParser (string path)
{
this.path = path;
}
public void Parse()
{
// use file path here
}
}
// Execute Parse method using Thread
FileParser parser = new FileParser (myFile);
new Thread (new ThreadStart (parser.Parse)).Start();
2. Passing Parameters to threads with ParameterizedThreadStart
The ParameterizedThreadStart
delegate
takes object
as a parameter and returns void
.
// Method defined to be executed by the thread
static void Parse(object path)
{
// use file path here by casting it before usage
}
// Pass the argument in overloaded Thread.Start method
Thread t = new Thread (new ParameterizedThreadStart(Parse));
t.Start (myFile);
3. Waiting on the thread until given thread terminates Join
The below example show a classic example of Data Race condition, present in the code. Here the count variable is shared by the Main
Thread and the counterWork
Thread.
Thread.Join
pauses the main thread and waits for completion of the other threads.
Can you guess the output?
Is it 10? hmmm...
using System;
using System.Threading;
public class Test
{
static int count=0;
static void Main()
{
// Start the counter
ThreadStart counterWork = new ThreadStart(IncreementCounter);
Thread counterThread = new Thread(counterWork);
counterThread.Start();
for (int i=0; i < 5; i++)
{
count++;
}
// Waits for the counter thread to complete
counterThread.Join();
Console.WriteLine ("Final count: {0}", count);
}
static void IncreementCounter()
{
for (int i=0; i < 5; i++)
{
count++;
}
}
}
The output seems to be straightforward, 10 feels right everytime. It's not guaranteed to be 10.
- Increment Condition:
Both Threads might read the old value forcount
variable, thus making it out of date.
Three steps for increment condition.- Read the value
- Increment the value
- Update the value
- Atomicity
Above scenario violates Atomicity. Atomicity states the operations as atomic,- Atomic write: you can't have another thread reading the value halfway through the write.
- Atomic read: you can't have another thread changing the value halfway through the read.