对 Task.ContinueWith 使用异步回调
本文关键字:异步 回调 Task ContinueWith | 更新日期: 2023-09-27 18:30:36
我正在尝试使用C#的async/await/continuewith。我的目标是必须有 2 个并行运行的任务,尽管哪个任务正在按顺序执行一系列操作。为此,我计划有一个表示并行运行的 2 个(或更多)任务的List<Task>
,并在每个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
的任务等待提供的功能完成,然后再被视为完成?等待将等待两个任务完成,原始任务和继续返回
使用 ContinueWith
方法链接多个任务时,返回类型将是Task<T>
,而T
是传递给ContinueWith
的委托/方法的返回类型。
由于异步委托的返回类型是 Task
,您最终会得到一个Task<Task>
,并最终等待异步委托返回在第一次await
之后完成的Task
。
为了纠正这种行为,您需要使用嵌入在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");
}
}