运行任务顺序,一个接一个

问题描述 投票:7回答:3

我有一系列任务,其中每个任务都取决于前一个任务的输出。我想将其表示为单个Task对象,其结果是序列结尾的输出。 (如果任务不相互依赖,那么我可以并行执行,并使用TaskFactory.ContinueWhenAll。)

我希望能够实现此方法:

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

如何有效地依次运行任务?我正在使用C#4.0,所以无法使用async / await替我做。

编辑:我可以这样写AggregateAsync

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);
            });
}

但是我肯定会得到一批任务,每个任务同步地等待之前的任务?

c# .net c#-4.0 asynchronous
3个回答
9
投票

简单方法(使用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;
}

困难的方式:

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;
}

请注意,采用“硬性”方式进行错误处理更为尴尬;第一个项目的异常将由每个后续项目包装在AggregateException中。 “简单”方式不会包装此类异常。


4
投票

您可以使用Task.ContinueWith。在下面的代码中看到的task代表上一个(已完成)任务,您可以获取其结果以执行第二个任务,依此类推。

T item1 = default(T);
T item2 = default(T);
Task<TState> task1 = makeTask(initial, item1);

//create second task
task1.ContinueWith(task => makeTask(task.Result, item2).Result,
                     TaskContinuationOptions.OnlyOnRanToCompletion);

编辑

对不起,我错过了这一部分

我想将其表示为单个Task对象,其结果是序列末尾的输出。

为了做到这一点,您只需要返回对上一个ContinueWith调用结果的引用。

Task<State> aggregate = task1.ContinueWith(
                                  task => makeTask(task.Result, item2).Result,
                                  TaskContinuationOptions.OnlyOnRanToCompletion);

var res = aggregate .Result; //wait synchronously for the result of the sequence
© www.soinside.com 2019 - 2024. All rights reserved.