任务和异步之间的区别

时间:2014-04-10 19:41:14

标签: c# .net asynchronous clr

C#提供了两种创建异步方法的方法:

方法1:

static Task<string> MyAsyncTPL() {
  Task<string> result = PerformWork();
  return result.ContinueWith(t => MyContinuation());
}

方法2:

static async Task<string> MyAsync() {
  string result = await PerformWork();
  return MyContinuation();
}

上述两种方法都是异步的并且实现了同样的目的。那么,我什么时候应该选择一种方法呢?是否有使用其中一个的指导方针或优点?

2 个答案:

答案 0 :(得分:17)

我建议您使用await而不是ContinueWith。虽然 - 在高层次 - 它们非常相似,但它们也有不同的默认行为。

使用ContinueWith时,您选择的是较低级别的抽象。特别是,这里有一些“危险点”,这就是为什么我不建议使用ContinueWith,除非方法非常简单(或者你的名字是Stephen Toub):

  • async Task方法引发的异常放在返回的任务上;非async方法引发的异常会直接传播。
  • 默认情况下,
  • await将在同一“上下文”中恢复async方法。此“上下文”为SynchronizationContext.Current,除非它是null,在这种情况下它是TaskScheduler.Current。这意味着如果在UI线程上(或在ASP.NET请求上下文中)调用MyAsync,那么MyContinuation也将在UI线程上执行(或在相同的ASP.NET请求上下文中) 。我更多地解释了on my blog
  • 您应始终为ContinueWith指定调度程序;否则,它会拾取TaskScheduler.Current,这可能会导致令人惊讶的行为。我详细描述了这个问题on my blog。该帖子约为StartNew;但是ContinueWith在该帖子中描述了相同的“非默认默认调度程序”问题。
  • await使用ContinueWith中默认未设置的相应行为和优化标志。例如,它使用DenyChildAttach(以确保异步任务不会被错误地用作并行任务)和ExecuteSynchronously(优化)。

简而言之,将ContinueWith用于异步任务的唯一原因是保存极其少量的时间和内存(通过避免async状态机开销) ,作为交换,您的代码不太可读和可维护。

用一个非常简单的例子,你可能会侥幸逃脱;但正如Jon Skeet指出的那样,只要你有循环,ContinueWith代码就会在复杂性方面爆炸。

答案 1 :(得分:9)

await基本上是延续的简写,默认情况下使用相同的同步上下文来继续。

对于像你这样的非常简单的例子,使用await时没有很多的好处 - 尽管异常的包装和解包会产生更一致的方法。

但是,当您获得更复杂的代码时,async会产生巨大的差异。想象一下你想要的:

static async Task<List<string>> MyAsync() {
    List<string> results = new List<string>();
    // One at a time, but each asynchronously...
    for (int i = 0; i < 10; i++) {
        // Or use LINQ, with rather a lot of care :)
        results.Add(await SomeMethodReturningString(i));
    }
    return results;
}

......手动延续会变得更加毛茸茸。

此外,async / await可以使用Task / Task<T>以外的其他类型,只要它们实现适当的模式。

值得更多地了解它在幕后所做的事情。您可能希望从MSDN开始。