使用Task.ContinueWith的异步回调

时间:2013-09-09 11:25:20

标签: c# async-await

我正在尝试使用C#的async / await / continuewith。 我的目标是必须有两个并行运行的任务,尽管哪个任务按顺序执行一系列操作。 为此,我计划让List<Task>代表并行运行的2个(或更多)任务,并在每个ContinueWith上使用Task 我的问题是,当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被调用的任务等待提供的函数在被认为完成之前完成?即。 .Wait将等待两个任务完成,原始任务和ContinueWith返回的任务

3 个答案:

答案 0 :(得分:37)

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

由于异步委托的返回类型为Task,您最终会得到Task<Task>并最终等待异步委托返回Task已完成在第一个await之后。

要纠正此行为,您需要使用Task中嵌入的返回Task<Task>。使用Unwrap扩展名方法将其解压缩。

答案 1 :(得分:24)

当您进行async编程时,您应该努力将ContinueWith替换为await,因此:

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)。它们并非旨在协同工作。

答案 2 :(得分:5)

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

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");
    }
}