使用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
方法链接多个任务时,返回类型将为Task
而T
是传递给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"); } }