我有一系列任务,每个任务都取决于前一个任务的输出。我想将它表示为单个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);
});
}
但是我肯定会得到一批任务,每个任务都会同步等待前一个任务?
答案 0 :(得分:6)
简单方法(使用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
中。 “简单”的方式不会包含这样的异常。
答案 1 :(得分: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