Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run sequence of tasks, one after the other

I have a sequence of tasks, where each one depends on the output of the previous one. I'd like to represent this as a single Task object, whose result is the output of the end of the sequence. (If the tasks didn't depend on one another then I could do it in parallel and I would use TaskFactory.ContinueWhenAll.)

I'd like to be able to implement this method:

static Task<TState> AggregateAsync<T, TState>(
    IEnumerable<T> items,
    TState initial,
    Func<TState, T, Task<TState>> makeTask);

How can I efficiently run the tasks one after another in sequence? I'm using C# 4.0 so I can't use async/await to do it for me.

Edit: I could write AggregateAsync like this:

static Task<TState> AggregateAsync<T, TState>(IEnumerable<T> items, TState initial, Func<TState, T, Task<TState>> makeTask)
{
    var initialTask = Task.Factory.StartNew(() => initial);
    return items.Aggregate(
        initialTask,
        (prevTask, item) =>
            {
                prevTask.Wait(); // synchronous blocking here?
                return makeTask(prevTask.Result, item);
            });
}

But surely I'll get a batch of tasks, each of which blocks synchronously waiting for the one before it?

like image 888
Tim Robinson Avatar asked Dec 06 '13 10:12

Tim Robinson


People also ask

Which code creates a chain of task to be executed after one another?

c# - Run sequence of tasks, one after the other - Stack Overflow. Stack Overflow for Teams – Start collaborating and sharing organizational knowledge.

Do tasks run in parallel?

Concurrent tasks progress at the same time in the worker system but they don't progress simultaneously. Parallel tasks are executed by different workers at the same time. Concurrency refers to how a worker system handles multiple tasks while parallelism refers to how a worker system handles a single task.

Which of the below statement creates a task that will complete when all of the supplied tasks have completed?

WhenAll(IEnumerable<Task>) Creates a task that will complete when all of the Task objects in an enumerable collection have completed.


1 Answers

The easy way (using Microsoft.Bcl.Async):

static async Task<TState> AggregateAsync<T, TState>(
    this IEnumerable<T> items,
    TState initial,
    Func<TState, T, Task<TState>> makeTask)
{
  var state = initial;
  foreach (var item in items)
    state = await makeTask(state, item);
  return state;
}

The hard way:

static Task<TState> AggregateAsync<T, TState>(
    this IEnumerable<T> items,
    TState initial,
    Func<TState, T, Task<TState>> makeTask)
{
  var tcs = new TaskCompletionSource<TState>();
  tcs.SetResult(initial);
  Task<TState> ret = tcs.Task;
  foreach (var item in items)
  {
    var localItem = item;
    ret = ret.ContinueWith(t => makeTask(t.Result, localItem)).Unwrap();
  }
  return ret;
}

Note that error handling is more awkward with the "hard" way; an exception from the first item will be wrapped in an AggregateException by each successive item. The "easy" way does not wrap exceptions like this.

like image 81
Stephen Cleary Avatar answered Oct 14 '22 09:10

Stephen Cleary