链接两个函数() - >任务<A>和A->任务<B>

Bja*_*ert 10 .net c# .net-4.0 task-parallel-library

我不知道我是否以错误的方式思考TPL,但我很难理解如何获得以下内容:

我有两个功能

Task<A> getA() { ... }
Task<B> getB(A a) { ... }
Run Code Online (Sandbox Code Playgroud)

这似乎经常发生:我可以异步获得A.并且给定A,我可以异步获得B.

我无法弄清楚在TPL中将这些功能链接在一起的正确方法.

这是一次尝试:

Task<B> Combined()
{
    Task<A> ta = getA();
    Task<Task<B>> ttb = ta.ContinueWith(a => getB(a.Result));
    return ttb.ContinueWith(x => x.Result.Result);
}
Run Code Online (Sandbox Code Playgroud)

ContinueWith是我感到困惑的地方.返回的类型是"双任务",Task<Task<B>>.这对我来说似乎有些不对劲.

更新2011-09-30:

巧合的是,我找到了TaskExtensions.Unwrap对a Task<Task<T>>进行操作的扩展方法Task<T>.所以在我们得到C#5.0之前,我可以在这样的情况下执行ta.ContinueWith(a => ...).UnWrap(),其中continuation本身返回一个任务.

Jon*_*eet 14

getB 必须是一个返回的方法Task<B>而不是B吗?

问题ContinueWith是:

public Task<TNewResult> ContinueWith<TNewResult>(
    Func<Task<TResult>, TNewResult> continuationFunction,
    CancellationToken cancellationToken
)
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下,因为getB回报Task<B>,你是在传递Func<Task<A>, Task<B>>,所以TNewResultTask<B>.

如果您可以更改getB为仅返回B给定的a A,那将起作用...或者您可以使用:

return ta.ContinueWith(a => getB(a.Result).Result);
Run Code Online (Sandbox Code Playgroud)

然后lambda表达式将是类型,Func<Task<A>, B>因此ContinueWith将返回a Task<B>.

编辑:在C#5中你可以轻松地写:

public async Task<B> CombinedAsync()
{
    A a = await getA();
    B b = await getB(a);
    return b;
}
Run Code Online (Sandbox Code Playgroud)

......所以这只是"只是"解决最终结果的问题.我怀疑它是这样的,但有错误处理:

public Task<B> CombinedAsync()
{
    TaskCompletionSource<B> source = new TaskCompletionSource();
    getA().ContinueWith(taskA => {
        A a = taskA.Result;
        Task<B> taskB = getB(a);
        taskB.ContinueWith(t => source.SetResult(t.Result));
    });
    return source.Task;
}
Run Code Online (Sandbox Code Playgroud)

那有意义吗?

  • `getB`没有_have_返回`Task <B>`,这很方便,因为它代表了一个异步操作.`getB(a).Result`的问题在于它会在`Task <A>`完成后立即阻塞一个线程,所以在我有数千个A和B的应用程序中,这不能很好地扩展. (2认同)

Ank*_*kur 6

如果您熟悉LINQ(以及它背后的Monad概念),那么下面是一个简单的Task monad,它允许您编写Tasks.

Monad实施:

public static class TaskMonad
    {
        public static Task<T> ToTask<T>(this T t)
        {
            return new Task<T>(() => t);
        }

        public static Task<U> SelectMany<T, U>(this Task<T> task, Func<T, Task<U>> f)
        {
            return new Task<U>(() =>
            {
                task.Start();
                var t = task.Result;
                var ut = f(t);
                ut.Start();
                return ut.Result;
            });
        }

        public static Task<V> SelectMany<T, U, V>(this Task<T> task, Func<T, Task<U>> f, Func<T, U, V> c)
        {
            return new Task<V>(() =>
            {
                task.Start();
                var t = task.Result;
                var ut = f(t);
                ut.Start();
                var utr = ut.Result;
                return c(t, utr);
            });            
        }
    }
Run Code Online (Sandbox Code Playgroud)

用法示例:

        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);
        }
Run Code Online (Sandbox Code Playgroud)