任务和异步之间的区别

Cha*_*ani 18 .net c# clr asynchronous

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

方法1:

static Task<string> MyAsyncTPL() {
  Task<string> result = PerformWork();
  return result.ContinueWith(t => MyContinuation());
}
Run Code Online (Sandbox Code Playgroud)

方法2:

static async Task<string> MyAsync() {
  string result = await PerformWork();
  return MyContinuation();
}
Run Code Online (Sandbox Code Playgroud)

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

Ste*_*ary 17

我建议你使用await而不是ContinueWith.虽然 - 在高级别 - 它们非常相似,但它们也有不同的默认行为.

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

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

简而言之,ContinueWith用于异步任务的唯一原因是节省少量的时间和内存(通过避免async状态机开销),并且作为交换,您的代码不易读取和维护.

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


Jon*_*eet 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;
}
Run Code Online (Sandbox Code Playgroud)

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

另外,async/ await可以使用除Task/ 之外的类型Task<T>,只要它们实现适当的模式.

值得一读的更多关于它在幕后做的事情.您可能想从MSDN开始.