Asynchronous programming with TaskCompletionSource

Asynchronous programming was the main improvement introduced in C# 5 in the late 2012.

The class and methods introduced are conformed to a model which that is easily to implement asynchronous operations and it follow some rules and specifications knows as Task-based Asynchronous Pattern.

One of the class that we can use is the TaskCompletionSource that I have recently used, and so I want to shared what I have understood about that.

Introduction

This class give us the ability to handle the state of the Task that it’s returned to the consumer, for example when we deal with tasks that will be delayed or with external asynchronous operations like downloads when we want to propagate the result at the end of those.

With this feature we can return an incompleted tasks to the consumer and manage them at the end of the internal operations.

This approach is also useful and optimized when we don’t want to allow the consumers to transition the state without access to the TaskCompletitionSource.

With this introduction, we are ready to a pratical application of this class.

Using

In my example I have a simple task that after a delay returns the id of the current execution thread:

public Task<int> Task1(CancellationTokenSource cancellationTokenSource)
{
return Task.Run(async () =>
{
if (cancellationTokenSource == null)
throw new ArgumentNullException(nameof(cancellationTokenSource));

await Task.Delay(1000, cancellationTokenSource.Token);
return Thread.CurrentThread.ManagedThreadId;
});
}

It’s pretty simple, the method accepts a CancellationTokenSource and returns a started task that after 1 second give back the id of the execution thread.

Now I implement a second method, that will use this method in conjunction with the TaskCompletionSource:

public Task<int> TaskCompletitionSource1(CancellationTokenSource cancellationTokenSource)
{
var tcs = new TaskCompletionSource<int>();

int threadId = 0;
Task.Run(async () =>
{
threadId = await Task1(cancellationTokenSource);
}).ContinueWith(t => tcs.SetResult(threadId), TaskContinuationOptions.OnlyOnRanToCompletion);

return tcs.Task;
}

As you can see, I create a new instance of the TaskCompletionSource and in the last row I return the Task property to the caller, that will have a state WaitingForActivation.

In the method I run a new task that will invoke the Task1 method and then leverage the ContinueWith method to set the result of the TaskCompletionSource with the SetResult method.

With this approach I’m sure that the customer will receive the result of the task at the end of the external operations.

The customer can use this implementation like this:

var cancellationTokenSource = new CancellationTokenSource();
var result = await _mainService.TaskCompletitionSource1(cancellationTokenSource);

The using is exactly the same of a common Task and transparent to the final user.

You can find a project example here.

 

 

 

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s