如何等待直到Task.ContinueWith内部任务完成

9
请看这段代码:
    private async Task InnerTask(bool outerTaskResult)
    {
        Console.WriteLine("2");
        await Task.Factory.StartNew(() => Thread.Sleep(10000));
        Console.WriteLine("3");
    }

    private async void Button2_Click(object sender, RoutedEventArgs e)
    {
        var task = Task.FromResult(false);
        Task<Task> aggregatedTask = task.ContinueWith(task1 => InnerTask(task1.Result));
        Console.WriteLine("1");
        await aggregatedTask;
        Console.WriteLine("4");
    }

期望的输出是:
1
2
3
4

但我得到了:
1
2
4
3

这可能与 InnerTask 在不同的线程上执行有关。我使用 ContinueWith,因为原始代码中的任务是动态创建并以此方式排队的。使用 .Wait() 方法(见下文)可以工作,但我认为这是一个坏主意,因为该方法会阻塞。
task.ContinueWith(task1 => InnerTask(task1.Result).Wait())

这里的正确方法是什么?
1个回答

10

您可以使用TaskExtensions.Unwrap()(这是Task<Task>的扩展方法)来取消外部任务并检索内部任务:

private async void Button2_Click(object sender, RoutedEventArgs e)
{
    var task = Task.FromResult(false);
    Task aggregatedTask = task.ContinueWith(task1 => InnerTask(task1.Result)).Unwrap();
    Console.WriteLine("1");
    await aggregatedTask;
    Console.WriteLine("4");
}

请注意,为了简化整个过程,您可以使用await等待任务完成,而不是使用ContinueWith风格的继续操作:

private async void Button2_Click(object sender, RoutedEventArgs e)
{
    var task = Task.FromResult(false);

    Console.WriteLine("1");

    var result = await task;
    await InnerTask(result);

    Console.WriteLine("4");
}

“Unwrap” 实际上是不必要的。问题中 await aggregatedTask 的返回值实际上是内部任务。 - Panagiotis Kanavos
2
@PanagiotisKanavos 确实,这不是必须的,你也可以await两次。这两种方法都会做同样的事情。这当然是一种适当的方法。在C# 5.0中,您实际上可以将Unwrap实现为:public static async Task Unwrap(this Task<Task> task){ await await task; } (C# 4.0版本实际上非常冗长和乏味。) - Servy
请注意,您的第二个代码片段与原始帖子的代码并不完全相同。他的代码存在竞争条件,允许12以任意顺序发生。您的第二个代码片段强制执行严格的顺序。换句话说,它没有并行化OP并行化的一些操作,而他可能希望这两个操作能够并行进行。 - Servy
@YuvalItzchakov,你只需要将打印“1”的行移到await task之前(而不是在其右侧),以保持OP的原始语义。 - Servy
@YuvalItzchakov 当然你需要一个内部任务也是非泛型的版本,这基本上是复制粘贴并更改仅有的两个小表达式。 - Servy
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接