- The TPL is a set of types and APIs in the
System.Threading
and System.Threading.Tasks
namespaces.
- It scales the degree of concurrency dynamically to most efficiently use all the processors that are available.
- It handles the partitioning of the work, the scheduling of threads on the
ThreadPool
, cancellation support, and state management.
- However, a
Task
does not map directly to an unmanaged thread.
- Rather, the
Task
provides a degree of abstraction to the underlying unmanaged thread construct.
- Instead of creating an operating system thread each time a
Task
is created, the Task
requests a thread from the thread pool.
- The thread pool evaluates whether to create an entirely new thread or to allocate an existing thread (such as one that previously finished executing) to the
Task
request.
- A task scheduler is responsible for starting the Task and managing it.
- By default, the Task scheduler uses threads from the thread pool to execute the Task.
Task
manages when to return a thread to the thread pool for later reuse and when to deallocate the thread and release any resources it may be consuming.
- Notice that following the Task declaration there is a call to.
- When using
Task
, the Action
specified to Task
doesn’t start executing before Start()
method get called.
- Additionally, the call to
Wait()
forces the main thread to stop and “Wait” until all the work assigned to task has completed executing.
- If the work executed in the task returns a result, then any request for the
Result
will automatically block until the task completes.
const int repetitions = 10000;
Task task = new Task(() =>
{
for (var i = 0; i < repetitions; i++)
Console.Write('-');
});
task.Start();
for (var i = 0; i < repetitions; i++)
Console.Write('.');
task.Wait();
- Instead of using
task.Start()
, you can use the StartNew()
method of the static Factory property on Task.
- The result is similar to instantiating the Task except that the return from
Task.Factory.StartNew<TResult>()
is already started.
Task<string> task = Task.Factory.StartNew<string>(() => PiCalculator.Calculate(100));
Task
Properties
Status
: It returns a System.Threading.Tasks.TaskStatus
enum indicating the status of the task.
- Options:
Created
, WaitingForActivation
, WaitingToRun
, Running
, WaitingForChildrenToComplete
, RanToCompletion
, Canceled
, and Faulted
.
IsCompleted
: It is set to true when a task completes whether it faulted or not.
- It is true whenever the Status is
RanToCompletion
, Canceled
, or Faulted
.
Id
: It is a unique identifier of the task.
- This is especially useful in debugging when trying to work through multi-threading problems such as race and deadlocks.
AsyncState
: This property can track additional data.
- For example, imagine a List<T> of values that various tasks are calculating.
- One way to place the result into the correct location of the list is to store the list index targeted to contain the result into the
AsyncState
property.
- This way, when the task completes, the code can index into the list using the
AsyncState
.
Task.CurrentId
: It is a static property on the Task
that returns an identifier for the currently executing Task (the one executing the Task.CurrentId call).
- Since the property is static, it is available anywhere and is mostly useful for debugging and diagnostic-type activities.
- This method is for chaining tasks together such that as soon as the first one in the chain completes it triggers the ones that have registered to begin executing after it.
- The default continuation option indicates continue asynchronously with no special task options.
- It specifies that the continue-with-task should execute “when the antecedent task completes, regardless of the task’s final
TaskStatus
.
TaskContinuationOptions
LongRunning
specifies that a task will be a long-running, course-grained operation.
- It provides a hint to the
TaskScheduler
that over-subscription may be warranted.
- If you know that a
Task
is going to be long-running, you need to notify the thread pool that it is unlikely to return the shared thread anytime soon.
- This allows the thread pool to increase the likelihood of creating a dedicated thread for the task, rather than pulling from the shared threads.
- Technically,
LongRunning
is actually something that the scheduler needs to take into consideration.
- However, since the default scheduler is the
ThreadPoolTaskScheduler
, it is the thread pool that takes the long-running parameter into consideration.