Using the Task Parallel Library for
Asynchronous Programming
Filip Ekberg
Principal Consultant & CEO
@fekberg [Link]
Introducing the Task
[Link](() => {
// Heavy operation to run somewhere else
});
Using Tasks without async & await
Obtain the result
Capture exceptions
Running continuations depending on success or failure
Cancelling an asynchronous operation
Read File Content Asynchronously
using var stream =
new StreamReader([Link]("file")));
var fileContent = await [Link]();
Using the Task
var response = await [Link](URL);
Returns a Task
Result of Awaits the Task
the operation
Task from the Task Parallel Library
Represents a single asynchronous operation
Functionality Provided by the Task
Execute work on a different thread
Get the result from the asynchronous operation
Subscribe to when the operation is done by introducing a
continuation
It can tell you if there was an exception
Introducing the Task
[Link](() => { /* Heavy operation */ });
[Link](SomeMetodMethod);
Introducing the Task
Queue this anonymous method
on the thread pool for execution
[Link](() => { /* Heavy operation */ });
[Link](SomeMetodMethod);
Queue this method
on the thread pool for execution
Generic vs Non-Generic [Link]
Task<T> task = [Link]<T>(() => {
return new T();
});
Task task = [Link](() => { });
Generic vs Non-Generic [Link]
Task<T> task = [Link]<T>(() => {
return new T(); An asynchronous operation
}); that returns a value
Task task = [Link](() => { });
Generic vs Non-Generic [Link]
Don’t need to explicitly
use [Link]<T>()
Task<T> task = [Link](() => {
return new T();
});
Task task = [Link](() => { });
Avoid queuing
heavy work back
on the UI thread
Obtaining the Result of a Task
Introduce a Continuation
var task = [Link](() => { });
var continuationTask =
[Link]((theTaskThatCompleted) => {
// This is the continuation
// which will run when “task” has finished
});
Introduce a Continuation
var task = [Link](() => { });
var continuationTask =
[Link]((theTaskThatCompleted) => {
// This is the continuation
// which will run when This
“task” has finished
continuation will NOT
execute on the original thread
});
Introduce a Continuation
var task = [Link](() => { });
These two are the same!
[Link]((theTaskThatCompleted) => {
// This is the continuation
});
Multiple Continuations
var task = [Link](() => { });
[Link]((t) => { /* Continuation 1 */ });
[Link]((t) => { /* Continuation 2 */ });
[Link]((t) => { /* Continuation 3 */ });
[Link]((t) => { /* Continuation 4 */ });
[Link]((t) => { /* Continuation 5 */ });
async & await is a much more
readable and maintainable
approach
Continuation Differences
[Link](_ => {
// This continuation executes asynchronously
// on a different thread
});
await task;
// This continuation executes on the original context
async & await may be
unnecessary in certain
situations
Demo
Demo: Nested asynchronous operations
Asynchronous Anonymous Methods
// Thread 1
[Link](async () => {
// Thread 2
await [Link](() => {
// Thread 3
});
// Thread 2
});
// Thread 1
Asynchronous anonymous
methods are NOT the same
as async void
Next: Handling Task Success and Failure
Handling Task Success and Failure
Continuing After an Exception
var loadLinesTask = [Link](() => {
throw new FileNotFoundException();
});
[Link]((completedTask) => {
// Running this may be unnecessary
// if you expect [Link]!
});
ContinueWith executes when
the Task completes no
matter if it’s successful,
faulted or cancelled
The Continuation Did Not Fail
[Link](() => {
throw new FileNotFoundException();
Faulted with attached exception!
})
.ContinueWith((completedTask) => {
})
.ContinueWith((completedContinuationTask) => {
}) Not faulted!
OnlyOnRanToCompletion
Task has no exceptions Task was not cancelled
await it will not throw an
aggregate exception
Always Validate Your Tasks
You can use async & await You can chain a continuation
using ContinueWith
TaskContinuationOptions
Specifies the behavior for a task that is created by using the ContinueWith
Continuing After an Exception
var loadLinesTask = [Link](() => {
throw new FileNotFoundException();
});
[Link]((completedTask) => {
// will always run
});
[Link]((completedTask) => {
// will not run if completedTask is faulted
}, [Link]);
Always validate your
asynchronous operations
Handling Exceptions
try
{
await task;
}
catch(Exception ex)
{
// log [Link]
}
[Link]((t) => {
// log [Link]
}, [Link]);
Next: Cancellation and Stopping a Task
Cancellation and Stopping a Task
Don’t force a user to wait for
a result they know is
incorrect.
Allow them to cancel!
CancellationTokenSource
Signals to a CancellationToken that it should be canceled.
Always call Dispose() on the cancellation token source after the
asynchronous operation completed
Cancellation Token Source
CancellationTokenSource cancellationTokenSource;
[Link]();
[Link](5000);
Cancellation Token Source
CancellationTokenSource cancellationTokenSource;
[Link]();
[Link]();
Signals to a Cancellation Token
that it should cancel
[Link](5000);
Cancellation Token Source
CancellationTokenSource cancellationTokenSource;
[Link]();
[Link](5000);
...
[Link](); Schedules a cancellation that
occurs after 5 seconds
Cancellation Token
CancellationTokenSource cancellationTokenSource;
CancellationToken token = [Link];
[Link](() => {}, token);
[Link](() => {
if([Link]) {}
});
Cancellation Token
CancellationTokenSource cancellationTokenSource;
CancellationToken token = [Link];
[Link](() => {}, token);
[Link](() => {
if([Link]) {}
});
Calling Cancel will not
automatically terminate the
asynchronous operaiton
Cancellation
CancellationTokenSource cancellationTokenSource;
CancellationToken token = [Link];
[Link]();
Will not start if Cancellation
Token is marked as Cancelled
[Link](() => {}, token);
Cancellation Token and
ContinueWith
CancellationTokenSource cancellationTokenSource;
CancellationToken token = [Link];
var task = [Link](() => {}, token);
[Link]((t) => {}, token);
Task Status
var token = new CancellationToken(canceled: true);
var task = [Link](() => "Won’t even start", token);
[Link]((completingTask) => {
// [Link] = Canceled
});
[Link]((completingTask2) => {
// [Link] = Canceled
});
Task Status
var token = new CancellationToken(canceled: true);
var task = [Link](() => "Won’t even start", token);
[Link]((completingTask) => {
// [Link] = Canceled
})
.ContinueWith((continuationTask) => {
// [Link] = RanToCompletion
});
Demo
Example: Cancellation with HttpClient
Every library could handle
cancellations differently
Task Parallel Library
async Task Process(CancellationToken token)
{
var task = [Link](() => {
// Perform an expensive operation
return ... ;
}, token);
var result = await task;
// Use the result of the operation
}
ContinueWith
var task = [Link](() => {
return ... ;
});
[Link]((completedTask) => {
// Continue..
});
ContinueWith
var task = [Link](() => {
return ... ;
});
[Link]((completedTask) => {
// Continue.. Asynchronous operation
executed on a different thread
});
Cross-Thread Communication
var task = [Link](() => {
return ... ;
});
[Link]((completedTask) => {
[Link](() => { /* Run me on the UI */ });
});
Be careful!
What happens if the method
you point to forces itself onto
the UI/calling thread?
Introducing Asynchronous Methods
Implement two versions of the Do not wrap the synchronous
method if you need both an method in a [Link] just to make
asynchronous and synchronous the code asynchronous. Copy the
versioon code to the asynchronous method
and implement it properly
Task Continuation Options
var task = [Link](() => {
throw new FileNotFoundException();
});
[Link]((completedTask) => {
// will not run if completedTask is faulted
}, [Link]);
Introducing a Task with [Link] to run
work on a different thread
Summary Obtaining the result and exceptions in the
continuation of a Task
Configure the continuation to only run on
success, failure or a cancellation
How to combine async and await with your
own asynchronous operations
Understand the difference between await
and ContinueWith
Next: Exploring Useful Methods in the
Task Parallel Library