使用async和await关键字的好处

我是C#中异步方法的新手。 我已经读过这些关键字asyncawait帮助通过异步某些方法使程序更具响应性。 我有这个片段:

第一道路

  public static void Main() { Console.WriteLine("Hello!! welcome to task application"); Console.ReadKey(); Task ourtask = Task.Factory.StartNew(() => { return "Good Job"; }); ourtask.Wait(); Console.WriteLine(ourtask.Result); Console.ReadKey(); } 

第二种方式

  public static void Main() { Launch(); } public static async void Launch() { Console.WriteLine("Hello!! welcome to task application"); Console.ReadKey(); Console.WriteLine(await GetMessage()); Console.ReadKey(); } public static Task GetMessage() { return Task.Factory.StartNew(() => { return "Good Job"; }); } 

我需要知道 :

  1. 这两种实现之间是否存在差异(在并行性的概念中)?

  2. 如果我可以创建一个任务并等待它完成,那么使用asyncawait关键字有什么好处?

假设您有一个边界检查点。 每辆车都可以一个接一个地通过,让海关看看他们的车,看看他们是不是走私任何比利时巧克力。

现在假设你在你的大众甲壳虫中排成一行,在那里你几乎无法适应,在你成为一个24轮的monstertruck之前。 你现在被困在这个庞然大物的背后很长一段时间,直到海关完成所有搜索之后才能转移给你,他们基本上只需要拍下来告诉你你很好。

为了对抗这种效率,我们在边境巡逻队的好朋友有了一个想法并安装了第二个检查站。 现在他们可以传递两倍的人数而你可以只拿一个而不是等待monstertruck!

问题解决了吧? 不完全是。 他们忘了创建通往该检查站的第二条道路,因此所有交通仍然必须通过单行道,导致卡车仍然阻挡甲壳虫。

这与您的代码有什么关系? 很简单:你也是这样做的。

创建新Task您基本上创建了第二个检查点。 但是,当你现在使用.Wait()同步阻止它时,你就迫使每个人都走这条路。

在第二个示例中,您使用await来创建第二条道路并允许您的汽车与卡车同时处理。

我会尝试直接回答这些问题:

  1. 你的两个例子(实际上)都不涉及任何并行性。 我看到它们之间有两个主要区别:1)第一个例子将阻塞线程,而任务在第二个线程上运行,这是没有意义的,2)第二个例子将提前退出。 一旦遇到await ,控制立即返回Main() ,由于您没有等待Launch()返回的任务完成,您的程序将在此时退出。

  2. 使用asyncawait与等待任务完成的好处是await在该任务运行时不会阻止当前线程。 在引擎盖下,只要编译器遇到await ,它就会有效地将该方法的其余部分重写为将在完成任务时调用的回调。 这可以释放当前线程在任务运行时执行其他操作,例如响应客户端应用程序中的用户输入或服务Web应用程序中的其他请求。

坦率地说,这不是一个很好的例子来展示async / await的好处。 你基本上是说你想要做CPU限制工作,而且在完成这项工作之前你不想做任何其他事情。 你也可以同步这样做。 在进行I / O绑定工作时异步真的很shiny,例如通过网络进行调用(使用正确实现的异步库,如HttpClient ),因为你不是像第二个例子那样简单地将一个线程换成另一个线程; 从字面上看 ,这个I / O绑定的工作没有消耗线程 。

正如其他人所提到的,并行性完全是另一个话题。 虽然async / await可以用来帮助你实现它,但还有更多的参与,而且我认为你可以更好地掌握线程释放的好处,然后再继续“并行”。

正如其他人所提到的,这是一个很大的话题,我强烈建议你查看一些很棒的资源。 由于我已经引用了Stephen Cleary的博客,我将继续给它一个完整的插件 – 他的异步/等待介绍和随后的post是关于这个主题的一个很好的入门。

async / await清理过多利用Task.ContinueWith.ContinueWith.ContinueWith等的复杂代码。

从编码的角度来看,可视化,调试和维护Task.ContinueWith要困难得多,包括必须附带的相关exception处理。

所以, 等待来了,给了我们这个

  public static void Main() { Launch(); } public static async void Launch() { Console.WriteLine("Hello!! welcome to task application"); Console.ReadKey(); Console.WriteLine(await GetMessage()); Console.ReadKey(); } public static Task GetMessage() { return Task.Factory.StartNew(() => { return "Good Job"; }); } 

这几乎相当于:

  public static void Main() { Launch(); } public static async void Launch() { Console.WriteLine("Hello!! welcome to task application"); Console.ReadKey(); return Task.Factory.StartNew(() => GetMessage()) .ContinueWith((t) => { Console.WriteLine(t.Result) Console.ReadKey(); }); } public static Task GetMessage() { return Task.Factory.StartNew(() => { return "Good Job"; }); } 

您可以从示例中看到GetMessage() 之后的所有内容都包含在ContinueWith中,但是该方法会在创建任务后立即返回该任务。 所以它返回到调用方法。

在这里我们需要等待该任务,否则程序将继续退出:

 Launch().Wait(); 

不必编写ContinueWith()意味着我们的代码变得更具可读性,特别是在我们必须在单个方法中将多个await块链接在一起的情况下,它将“读取”正常。

同样如前所述,更好的exception处理由await示例处理,否则我们将不得不使用TPL方法来处理exception,这也可能使代码库复杂化。

关于你的两个例子,它们并不是真正等同的,所以你不能真正地判断一个。 但是, async / await等同于构造Tasks / ContinueWith。

我认为async / await是TPL演变为实际语言本身。 一种语法糖。

我们有async / await编程的两个主要好处

1-非阻塞编程

当你有长时间运行的操作,不需要阻止执行。 在这种情况下,您可以在等待长时间运行任务的结果时执行其他工作。

想象一下,我们有两个程序流程,它们可以并行工作而不会相互阻塞。

示例:假设我们需要记录每个错误,但同时不应阻止流程,因此在这种情况下我们可以同时记录并返回消息。

2-异步/等待编程中线程管理的好处

我们知道在正常编程(阻塞)中,每行代码都会阻塞它之后的所有内容,直到它完成进程,即使我们有不同的流(两个流没有任何依赖)。 但是在async / await编程中,应用程序不会阻止这个线程,换句话说,它们会释放它来做另一个工作,当函数完成工作时,任何自由线程都会处理响应。

C#异步并等待:我们为什么需要它们?