使用Task.ContinueWith的异步回调

我正试图用C#的async / await / continuewith来玩。 我的目标是必须有两个并行运行的任务,尽管哪个任务按顺序执行一系列操作。 为此,我计划有一个List代表并行运行的2个(或更多)任务,并在每个Task上使用ContinueWith我的问题是继续的回调似乎不会执行await taskList已经返回了。

为了总结,这里有一个示例来说明我期望发生的事情:

 class Program { static public async Task Test() { System.Console.WriteLine("Enter Test"); await Task.Delay(100); System.Console.WriteLine("Leave Test"); } static void Main(string[] args) { Test().ContinueWith( async (task) => { System.Console.WriteLine("Enter callback"); await Task.Delay(1000); System.Console.WriteLine("Leave callback"); }, TaskContinuationOptions.AttachedToParent).Wait(); Console.WriteLine("Done with test"); } } 

预期的产出是

 Enter Test Leave Test Enter callback Leave callback Done with test 

但是,输出是

 Enter Test Leave Test Enter callback Done with test 

有没有办法让在调用ContinueWith的Task上等待提供的函数在被认为完成之前完成? 即。 .Wait将等待两个任务完成,原始任务和ContinueWith返回的任务

使用ContinueWith方法链接多个任务时,返回类型将为TaskT是传递给ContinueWith的委托/方法的返回类型。

由于异步委托的返回类型是一个Task ,您将最终得到一个Task并最终等待异步委托返回在第一次await之后完成的Task

为了纠正这种行为,您需要使用嵌入在Task的返回Task 。 使用Unwrap扩展方法将其解压缩。

在进行async编程时,应该努力用await替换ContinueWith ,如下:

 class Program { static public async Task Test() { System.Console.WriteLine("Enter Test"); await Task.Delay(100); System.Console.WriteLine("Leave Test"); } static async Task MainAsync() { await Test(); System.Console.WriteLine("Enter callback"); await Task.Delay(1000); System.Console.WriteLine("Leave callback"); } static void Main(string[] args) { MainAsync().Wait(); Console.WriteLine("Done with test"); } } 

使用await的代码更清晰,更易于维护。

此外,您不应将父/子任务与async任务一起使用(例如, AttachedToParent )。 它们不是为了一起工作而设计的。

我想补充一下我的答案,以补充已经被接受的答案。 根据您尝试执行的操作,通常可以避免异步委托和包装任务的额外复杂性。 例如,您的代码可以像这样重新计算:

 class Program { static async Task Test1() { System.Console.WriteLine("Enter Test"); await Task.Delay(100); System.Console.WriteLine("Leave Test"); } static async Task Test2() { System.Console.WriteLine("Enter callback"); await Task.Delay(1000); System.Console.WriteLine("Leave callback"); } static async Task Test() { await Test1(); // could do .ConfigureAwait(false) if continuation context doesn't matter await Test2(); } static void Main(string[] args) { Test().Wait(); Console.WriteLine("Done with test"); } }