Continuation tasks in .NET TPL: many tasks continued by a single task

Tasks in .NET TPL make it easy to assign tasks that should run upon the completion of a certain task.

We’ll need a basic object with a single property:

public class Series
{
	public int CurrentValue
	{
		get;
		set;
	}
}

In previous installments of the posts on TPL we saw how to create tasks that are scheduled to run after a single task has completed: check here and here. It is also possible to create many tasks and a single continuation task that runs after all the antecedents have completed. The ContinueWhenAll() method takes an array or Task objects, all of which have to complete before the continuation task can proceed.

We create 10 tasks each of which increases the its local series value by 1000 in total in a loop:

Series series = new Series();

Task<int>[] motherTasks = new Task<int>[10];

for (int i = 0; i < 10; i++)
{
	motherTasks[i] = Task.Factory.StartNew<int>((stateObject) =>
	{
		int seriesValue = (int)stateObject;
		for (int j = 0; j < 1000; j++)
		{
			seriesValue++;
		}
		return seriesValue;
	}, series.CurrentValue);
}

All 10 tasks are antecedent tasks in the chain. Let’s declare the continuation task:

Task continuation = Task.Factory.ContinueWhenAll<int>(motherTasks, antecedents =>
{
	foreach (Task<int> task in antecedents)
	{
		series.CurrentValue += task.Result;
	}
});

continuation.Wait();
Console.WriteLine(series.CurrentValue);

We extract the series value from each antecedent task and increase the overall series value by the individual task results, i.e. a 1000. The lambda expression looks awkward a bit: the array of antecedent tasks appears twice. Once as the first argument to the ContinueWhenAll method and then as an input to the lambda expression.

There’s one more thing to note about the syntax: ContinueWhenAll of T denotes the return type of the antecedent tasks. You can also specify the return type of the continuation. Here’s a list of possibilities:

The continuation and the antecedent tasks both return an int:

Task<int> continuation = Task.Factory.ContinueWhenAll<int>(motherTasks, antecedents =>
{
	foreach (Task<int> task in antecedents)
	{
		series.CurrentValue += task.Result;
	}
	return 1234;
});

Continuation is void and antecedent tasks return an int:

Task continuation = Task.Factory.ContinueWhenAll<int>(motherTasks, antecedents =>
{
	foreach (Task<int> task in antecedents)
	{
		series.CurrentValue += task.Result;
	}
});

All of them are void:

Task continuation = Task.Factory.ContinueWhenAll(motherTasks, antecedents =>
{
	foreach (Task<int> task in antecedents)
	{
		//do something
	}
});

Continuation returns an int and antecedent tasks are void:

Task<int> continuation = Task<int>.Factory.ContinueWhenAll(motherTasks, antecedents =>
{
	foreach (Task<int> task in antecedents)
	{
		//do something
	}
	return 1234;
});

Note that it’s enough to start the antecedent tasks, which we did with Task.Factory.StartNew. The continuation task will be scheduled automatically.

View the list of posts on the Task Parallel Library here.

Advertisements

Continuation tasks in .NET TPL: continuation with many tasks

Tasks in .NET TPL make it easy to assign tasks that should run upon the completion of a certain task.

We’ll need a basic object with a single property:

public class Series
{
	public int CurrentValue
	{
		get;
		set;
	}
}

Declare and start a task that increases the CurrentValue in a loop and return the Series. This task is called the antecedent task in a chain of tasks.

Task<Series> motherTask = Task.Factory.StartNew<Series>(() =>
{
	Series series = new Series();
	for (int i = 0; i < 5000; i++)
	{
		series.CurrentValue++;
	}
	return series;
});

So this simple task returns a Series object with a current value property.

You can create other tasks that will continue upon the completion of the antecedent task. You can actually specify a condition when the continuation task should start. The default value is that the continuation task will be scheduled to start when the preceding task has completed. See the next post on TPL for examples.

You can combine continuations in different ways. You can have a linear chain:

  1. First task completes
  2. Second task follows first task and completes
  3. Third task follows second task and completes
  4. etc. with task #4, #5…

…or

  1. First task completes
  2. Second and thirds tasks follow first task and they all run
  3. Fourth task starts after second task and completes
  4. etc. with task #6, #7…

So you can create a whole tree of continuations as you wish. You can build up each continuation task one by one. Let’s declare the first continuation task:

Task<int> firstContinuationTask
	= motherTask.ContinueWith<int>((Task<Series> antecedent) =>
	{
		Console.WriteLine("First continuation task reporting series value: {0}", antecedent.Result.CurrentValue);
		return antecedent.Result.CurrentValue * 2;
	});

The first continuation task reads the Series object built by the antecedent, the “mother” task. It prints out the CurrentValue of the Series. The continuation task also returns an integer: the series value multiplied by two.

We’ll let a second continuation task print out the integer returned by the first continuation task. I.e. from the point view of the second continuation task the first continuation task is the antecedent. We can say that “motherTask” is the “grandma” 🙂

Task secondContinuationTask
	= firstContinuationTask.ContinueWith((Task<int> antecedent) =>
	{
		Console.WriteLine("Second continuation task reporting series value: {0}", antecedent.Result);
	});

Note that it’s enough to start the first task, which we did with Task.Factory.StartNew. The continuation tasks will be scheduled automatically.

Alternatively you can declare the continuation tasks in a chained manner:

motherTask.ContinueWith<int>((Task<Series> antecedent) =>
{
	Console.WriteLine("First continuation task reporting series value: {0}", antecedent.Result.CurrentValue);
	return antecedent.Result.CurrentValue * 2;
}).ContinueWith((Task<int> antecedent) =>
{
	Console.WriteLine("Second continuation task reporting series value: {0}", antecedent.Result);
});

View the list of posts on the Task Parallel Library here.

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: