Every System.Threading.Tasks.Task instance goes through a lifecycle, and it only makes this journey once. To provide insight into where in that lifecycle a given Task is, the Task class provides an instance Status property. That property returns a value from the TaskStatus enumeration that reflects the current point in the lifecycle.
“Current” is the operative word here. Many of the states are transient, meaning that we’re dealing with a concurrent system, and by the time you’ve received the value of the status, the status may have changed. However, the lifecycle does not contain cycles (as ironic as that may sound), meaning that there’s a directionality to the various states, and once a given state has been reached, the Task won’t return back to that state again or to any that came before it. Additionally, there are three “final” states, where once in those states, the Task is no longer executing and will no longer change state.
Here are the states and an informal description of what they imply:
|TaskStatus Enumeration Value||Informal Description|
|Created||This is the starting state for tasks created through a Task constructor. Tasks in this state will not leave the state until Start or RunSynchronously is called on the instance or until the Task is canceled.|
|WaitingForActivation||This is the starting state for tasks created through methods like ContinueWith, ContinueWhenAll, ContinueWhenAny, and FromAsync, as well as from a TaskCompletionSource<TResult>. The task isn’t currently scheduled, and won’t be until some dependent operation has completed (some tasks may never be scheduled, such as those created by a TaskCompletionSource<TResult> that have nothing relevant to be scheduled). For example, a task created by ContinueWith won’t be scheduled until the antecedent task (the one off of which ContinueWith was called) completes execution.|
|WaitingToRun||The task has been scheduled to a TaskScheduler and is waiting to be picked up by that scheduler and run. This is the starting state for tasks created through TaskFactory.StartNew; by the time the Task is returned from StartNew, it will already have been scheduled, and thus the state will be at least WaitingToRun (“at least”, since by the time StartNew returns, the Task could of course have already started or even completed executing).|
|Running||The Task is currently executing.|
|WaitingForChildrenToComplete||A Task isn’t considered complete until its attached children have completed. If a Task has finished executing its code body, it will leave the Running state, and if it’s then implicitly waiting for its children to complete, it will enter this state.|
|RanToCompletion||One of the three final states. A Task in this state has successfully completed execution, running to the end of its body without cancellation and without throwing an unhandled exception.|
|Canceled||One of the three final states. A Task in this state completed execution, but it did so through cancellation. To end in the Canceled state, a Task must either have cancellation requested prior to starting execution, or it must acknowledge a cancellation request during its execution.|
|Faulted||One of the three final states. A Task in this state completed execution due to an unhandled exception in its body or due to one of its attached children completing in this state.|
Several of these status values are also communicated through helper properties on the Task class. For a Task t:
- t.IsCanceled returns true if and only if t.Status equals TaskStatus.Canceled.
- t.IsFaulted returns true if and only if t.Status equals TaskStatus.Faulted. Additionally, t.Exception will be non-null if and only if t.IsFaulted is true.
- t.IsCompleted returns true if and only if t.Status equals TaskStatus.RanToCompletion, TaskStatus.Canceled, or TaskStatus.Faulted (i.e. one of the three final states). Note that IsCompleted does not just correspond to RanToCompletion, but rather to all final states.
Several operations in the Task Parallel Library are tied to the concept of a final state, or more generally to IsCompleted. For example, a call to the Wait method on a Task will not return until that Task’s IsCompleted is true. And the continuations for a Task will not fire until that same condition is met.