链接两个函数() – >任务和A->任务
我不知道我是否以错误的方式思考TPL,但我很难理解如何获得以下内容:
我有两个function
Task getA() { ... } Task getB(A a) { ... }
这似乎经常发生:我可以异步获得A.并且给定A,我可以异步获得B.
我无法弄清楚在TPL中将这些function链接在一起的正确方法。
这是一次尝试:
Task Combined() { Task ta = getA(); Task<Task> ttb = ta.ContinueWith(a => getB(a.Result)); return ttb.ContinueWith(x => x.Result.Result); }
ContinueWith
是我感到困惑的地方。 返回的类型是“双任务”, Task<Task>
。 这在某种程度上对我来说似乎不对。
更新2011-09-30:
巧合的是,我找到了对Task<Task>
进行操作的扩展方法TaskExtensions.Unwrap
来给出一个Task
。 所以在我们得到C#5.0之前,我可以在这样的情况下执行ta.ContinueWith(a => …)。UnWrap(),其中continuation本身返回一个任务。
你的getB
必须是一个返回Task
而不是B
吗?
问题是ContinueWith
是:
public Task ContinueWith ( Func, TNewResult> continuationFunction, CancellationToken cancellationToken )
所以在你的情况下,因为getB
返回Task
,你传入一个Func
,所以TNewResult
是Task
。
如果您可以将getB
更改为仅返回给定A
的B
,则可以使用…或者您可以使用:
return ta.ContinueWith(a => getB(a.Result).Result);
然后lambda表达式的类型为Func
因此ContinueWith
将返回一个Task
。
编辑:在C#5中你可以轻松地写:
public async Task CombinedAsync() { A a = await getA(); B b = await getB(a); return b; }
……所以这只是“只是”解决最终结果的问题。 我怀疑它是这样的,但有error handling:
public Task CombinedAsync() { TaskCompletionSource source = new TaskCompletionSource(); getA().ContinueWith(taskA => { A a = taskA.Result; Task taskB = getB(a); taskB.ContinueWith(t => source.SetResult(t.Result)); }); return source.Task; }
那有意义吗?
如果您熟悉LINQ(以及它背后的Monad概念),那么下面是一个简单的Task monad,它允许您编写Tasks。
Monad实施:
public static class TaskMonad { public static Task ToTask (this T t) { return new Task (() => t); } public static Task SelectMany(this Task task, Func> f) { return new Task (() => { task.Start(); var t = task.Result; var ut = f(t); ut.Start(); return ut.Result; }); } public static Task SelectMany(this Task task, Func> f, Func c) { return new Task(() => { task.Start(); var t = task.Result; var ut = f(t); ut.Start(); var utr = ut.Result; return c(t, utr); }); } }
用法示例:
public static void Main(string[] arg) { var result = from a in getA() from b in getB(a) select b; result.Start(); Console.Write(result.Result); }
如果您无法使用await
,您当然可以使用Unwrap
,但它会以次优的方式处理exception。 我喜欢的方法是Then
如本文所述 。 构图变得简单而优雅:
Task Combined() { return getA().Then(getB); }
对于那些对细节感兴趣的人,我刚才写了一篇关于组成异步方法的问题的博客文章 ,以及monad如何提供一个优雅的解决方案。
虽然接受的答案可能会奏效
Task Combined() { Task ta = getA(); Task ttb = ta.ContinueWith(a => getB(a.Result)).Unwrap(); return ttb; }
实现这一点是一种更优雅的方式。