C# 对Task.ContinueWith使用异步回调
我正在尝试使用C#的async/await/continuewith。 我的目标是必须有两个并行运行的任务,尽管哪个任务按顺序执行一系列操作。 为此,我计划创建一个C# 对Task.ContinueWith使用异步回调,c#,async-await,C#,Async Await,我正在尝试使用C#的async/await/continuewith。 我的目标是必须有两个并行运行的任务,尽管哪个任务按顺序执行一系列操作。 为此,我计划创建一个列表,表示两个(或更多)并行运行的任务,并在每个任务上使用ContinueWith 我的问题是,当wait taskList已经返回时,continue with中的回调似乎没有执行 为了总结,这里有一个示例来说明我期望发生的事情: class Program { static public async Task Test()
列表
,表示两个(或更多)并行运行的任务,并在每个任务上使用ContinueWith
我的问题是,当wait taskList
已经返回时,continue with中的回调似乎没有执行
为了总结,这里有一个示例来说明我期望发生的事情:
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
的任务在被视为已完成之前等待提供的函数完成?ie..Wait将等待两个任务完成,即原始任务和ContinueWith返回的任务使用ContinueWith
方法链接多个任务时,返回类型将为Task
,而T
是传递给ContinueWith
的委托/方法的返回类型
由于异步委托的返回类型是一个任务
,因此您将以一个任务
结束,并最终等待异步委托返回在第一个等待
之后完成的任务
为了纠正这种行为,您需要使用返回的任务
,该任务嵌入在任务
中。使用扩展方法将其提取。我想添加我的答案,以补充已被接受的答案。根据您尝试执行的操作,通常可以避免增加异步委托和包装任务的复杂性。例如,您的代码可以这样重新分解:
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");
}
}
在进行async
编程时,应努力将ContinueWith
替换为wait
,如下所示:
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");
}
}
使用wait
的代码更干净,更易于维护
此外,您不应将父/子任务与async
任务一起使用(例如,AttachedToParent
)。它们不是为一起工作而设计的。尝试从wait任务中删除wait
。延迟(1000)代码>确实有效。我删除了wait和async,因为没有等待。然而,我不知道它起作用的原因。你有理论上的解释吗?在“完成测试”之后是否会出现“离开回调”?调用task.ContinueWith(async(task)=>{..})
实际上返回任务,更多关于包装任务的信息。这正是您想要的吗?我的意思是,我会避免在不必要的地方使用包装任务。近7年后,我可以确认这是必要的步骤,而不是我在MS文档中找到的任何地方。我使用ContinueWith作为穷人的工作队列,奇怪的是queue=queue.ContinueWith(=>asyncFunc())
在Windows下似乎能按预期工作,在Linux上则不能,而且您肯定需要queue.ContinueWith(=>asyncFunc()).Unwrap()代码>。关键词有“似乎”-即使在没有展开()的Windows下,它实际上也不会按顺序运行任务,但它们之间有足够的延迟,足以让它工作得很好。@DylanNicholson不是开玩笑!我在一台Windows机器上做了几十次测试,我精心设计的“延续”似乎很好。直到我将相同的代码部署到基于Linux的Docker容器中,我才完全意识到需要展开Unwrap
;使用泛型的ContinueWith
有助于防止编译时出现意外行为,例如任务。如果ContinueWith
是async
并返回任务而不是ExpectedResult
,则ContinueWith(ContinueWith)
将无法编译,在被这项任务
行为和对晦涩的Unwrap()
的需求所折磨之后,我将返回并重构到简单的代码块,而不是继续。但是,值得注意的是,a)来自优秀的.Net开发人员的数百个示例使用continuation,但没有Unwrap
,b)他们自己将continuation
宣传为“相对容易使用,但功能强大且灵活”。这个特殊的晦涩难懂的情况有点令人沮丧…@mdisibio,有许多简单的单行程序,ContinueWith
+Unwrap
是合理的。有了这个组合,很容易正确地传播取消状态。例如:lolUnwrap()
toContinueWith
随着ConfigureAwait(false)
toawait
的发展而演变-易于添加,当您忘记添加它时会很烦人@mdisibio,更像是忘记了await
本身:)至于ConfigureAwait(false)
,现在我觉得它的相关性要小得多。我(不受欢迎)的意见是。@mdisibio(来自MS overview doc on continuations)专门描述了何时以及为什么使用Unwrap()
。该页其他部分中的示例也使用Unwrap
进行所有异步延续。如果我想启动并忘记一个异步方法,但仍然捕获并记录异常,那么该怎么办?“启动并忘记”是有问题的。您关心异常这一事实表明,它并不是真正的火与遗忘(因为它没有被遗忘)。对fire and forget的正确解决方案取决于代码到底在做什么。嗨,Stephen,在我的情况下,我很高兴有一个例外,只要我能记录它。然而,我对等待这个漫长的操作结束并不满意。@niproblema:“只要我能记录它”不是“忘记”。在这种情况下。