Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/303.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 对Task.ContinueWith使用异步回调_C#_Async Await - Fatal编程技术网

C# 对Task.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()

我正在尝试使用C#的async/await/continuewith。 我的目标是必须有两个并行运行的任务,尽管哪个任务按顺序执行一系列操作。 为此,我计划创建一个
列表
,表示两个(或更多)并行运行的任务,并在每个
任务上使用
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
是合理的。有了这个组合,很容易正确地传播取消状态。例如:lol
Unwrap()
to
ContinueWith
随着
ConfigureAwait(false)
to
await
的发展而演变-易于添加,当您忘记添加它时会很烦人@mdisibio,更像是忘记了
await
本身:)至于
ConfigureAwait(false)
,现在我觉得它的相关性要小得多。我(不受欢迎)的意见是。@mdisibio(来自MS overview doc on continuations)专门描述了何时以及为什么使用
Unwrap()
。该页其他部分中的示例也使用
Unwrap
进行所有异步延续。如果我想启动并忘记一个异步方法,但仍然捕获并记录异常,那么该怎么办?“启动并忘记”是有问题的。您关心异常这一事实表明,它并不是真正的火与遗忘(因为它没有被遗忘)。对fire and forget的正确解决方案取决于代码到底在做什么。嗨,Stephen,在我的情况下,我很高兴有一个例外,只要我能记录它。然而,我对等待这个漫长的操作结束并不满意。@niproblema:“只要我能记录它”不是“忘记”。在这种情况下。