可以使用async-await为您带来任何性能优势吗?

use*_*670 22 .net c# performance asynchronous async-await

每当我读到async- 时await,用例示例总是存在一个您不想冻结的UI.所有编程书籍/教程都是相同的,或者UI阻止是唯一的例子async- await作为开发人员我应该知道.

有没有人可以使用的例子async- await在算法中剔除性能优势?就像让我们采取任何经典的编程面试问题:

  • 在二叉树中查找最近的共同祖先
  • 给出a[0],a[1]......,a[n-1]表示基数为10的数字,找到使用相同数字的下一个最高数字
  • 找到两个排序数组的中位数(即如果你要合并它们的中位数)
  • 给定一组数字1,2......,n丢失一个数字,找到丢失的数字
  • 找到数组中最大的2个数字

有没有办法使用async- await具有性能优势?如果是这样,如果你只有1个处理器怎么办?那么你的机器不是只是在任务之间分配时间而不是同时真正地执行它们吗?

Har*_*lse 41

这次采访中,埃里克·利珀特(Eric Lippert)比较了异步等待与做早餐的厨师.它帮助我理解了async-await的好处.在中间某处搜索'async-await'

假设厨师必须做早餐.他必须烤面包,煮一些鸡蛋,也可以煮一些茶?

方法1:同步.由一个线程执行.你开始烘烤面包.等到面包烤好了.取出面包.开始加水,等到水沸腾,然后插入鸡蛋.等到鸡蛋准备好然后取出鸡蛋.开始为茶喝水.等到水煮沸后再煮茶.

你会看到所有的等待.当线程在等待它可以做其他事情.

方法2:Async-await,仍然是一个线程你开始烘烤面包.当面包被烤时,你开始为鸡蛋和茶开水.然后你开始等待.完成三个任务中的任何一个后,您将完成任务的第二部分,具体取决于首先完成的任务.因此,如果鸡蛋的水首先煮沸,你煮鸡蛋,并再次等待任何任务完成.

在这个描述中,只有一个人(你)正在做所有的事情.只涉及一个线程.好的方面是,因为只有一个线程在执行这些操作,所以代码看起来与读者非常同步,并且没有太多需要使您的变量线程安全.

很容易看出,这样你的早餐将在更短的时间内准备好(你的面包仍然会很温暖!).在计算机生活中,当线程必须等待另一个进程完成时,这些事情就会发生,例如将文件写入磁盘,从数据库或从Internet获取信息.这些通常是那种会看到函数的异步版本的函数:WriteWriteAsync,ReadReadAsync.

另外:在其他用户的一些评论和一些测试之后,我发现事实上它可以是任何线程在等待之后继续你的工作.这个其他线程具有相同的"上下文",因此可以表现为它是原始线程.

方法3:雇佣厨师烘烤面包,煮茶时煮鸡蛋:真正异步.几个线程这是最昂贵的选项,因为它涉及创建单独的线程.在制作早餐的例子中,这可能不会非常加快这个过程,因为相对较长的过程你无论如何都没有做任何事情.但是,例如,如果您还需要切片番茄,那么当您使用async-await执行其他操作时,让烹饪(单独的线程)执行此操作可能会很方便.当然,你做的其中一个等待厨师完成他的切片.

另一篇解释很多的文章是Async和Await写的那些有用的Stephen Cleary.


Ste*_*ary 18

每当我读到async-await时,用例示例总是存在一个您不想冻结的UI.

这是最常见的用例async.另一个是服务器端应用程序,async可以提高Web服务器的可伸缩性.

是否有任何示例可以使用async-await来优化算法中的性能优势?

没有.

如果要进行并行处理,可以使用任务并行库.并行处理是使用多个线程,在系统中的多个核心之间划分算法的部分.并行处理是一种并发形式(同时执行多个操作).

异步代码完全不同.异步代码的要点是在操作进行时使用当前线程.异步代码通常是I/O绑定或基于事件(如计时器).异步代码是另一种并发形式.

我在我的博客上有一个async介绍,以及关于如何async不使用线程的帖子.

请注意,任务并行库使用的任务可以调度到线程上并执行代码.基于任务的异步模式使用的任务没有代码,也没有"执行".虽然两种类型的任务都由相同的类型(Task)表示,但它们的创建和使用方式完全不同; 我在我的博客上更详细地描述了这些委托任务和承诺任务.

  • @Hendry只需要**任何**网络服务器目标论文,讨论异步I/O优于多线程和阻塞的优势,所有关键点基本相同.即检查http://stackoverflow.com/questions/14795145/how-the-single-threaded-non-blocking-io-model-works-in-node-js并精神上替换"回调","队列"和"事件" "with"tasks/continuations"和"schedulers" (2认同)

que*_*atl 6

简而言之,非常一般 - 不,它通常不会.但它需要更多的单词,因为"性能"可以通过多种方式理解.

仅当"作业"受I/O限制时,Async/await才会"节省时间".将它应用于受CPU限制的作业会引入一些性能命中.那是因为如果你有一些计算,即你的CPU需要10秒,那么添加async/await - 即:任务创建,调度和同步 - 只需将X额外的时间添加到你仍然需要刻录的10秒你的CPU完成工作.接近阿姆达尔定律的想法.不是真的,但非常接近.

然而,有一些'但是......

首先,由于引入异步/等待而导致的性能下降并不是那么大.(特别是如果你小心不要过头).

其次,由于async/await允许您更容易地编写I/O交错代码,您可能会注意到在您懒得(:)的地方删除I/O的等待时间的新机会,否则或者在没有异步/等待语法良好的情况下使代码难以遵循的地方.例如,围绕网络请求拆分代码是相当明显的事情,但您可能会注意到,即您还可以在您编写CSV文件或读取配置文件等的少数几个位置升级某些文件i/o.这里的增益不会归功于async/await - 这要归功于重写处理文件i/o的代码.你可以在没有异步/等待的情况下做到这一点.

第三,由于某些i/o操作更容易,您可能会注意到将CPU密集型工作卸载到另一个服务或机器上要容易得多,这也可以提高您的感知性能(缩短"挂钟"时间),但整体而言资源消耗将增加:添加另一台机器,花费在网络操作上的时间等.

第四:UI.你真的不想冻结它.在Tasks和async/await中包装I/O绑定和CPU绑定作业非常容易,并保持UI响应.这就是为什么你看到它随处可见.但是,虽然理想情况下I/O绑定操作应该是异步,但要在所有冗长的I/O上移除尽可能多的空闲等待时间,但CPU绑定的作业不需要分割或异步化而不仅仅是1水平下降.将巨大的单片计算工作包含在一个任务中就足以让UI解除阻塞.当然,如果你有许多处理器/核心,它仍然值得并行化内部的任何可能,但与I/O相反 - 分裂太多,你将忙于切换任务而不是咀嚼计算.

总结:如果你有时间进行I/O - 异步操作可以节省很多时间.很难过度异步I/O操作.如果您有CPU占用操作,那么添加任何东西将消耗更多的CPU时间和更多的内存,但由于将作业分成更小的部分,可以在同一个更多的核心上运行,因此挂钟时间可以更好时间.过度使用并不难,所以你需要小心一点.


And*_*rei 2

该方法在当前同步上下文上运行,并且仅当该方法处于活动状态时才使用线程上的时间。您可以使用 Task.Run 将 CPU 密集型工作移至后台线程,但后台线程对仅等待结果可用的进程没有帮助。

当您的应用程序中有一个 CPU 和多个线程时,您的 CPU 会在线程之间切换以模拟并行处理。使用 async/await ,您的异步操作不需要线程时间,因此您可以为应用程序的其他线程提供更多时间来完成工作。例如,您的应用程序(非 UI)仍然可以进行 HTTP 调用,您所需要的只是等待响应。这是使用 async/await 好处很大的情况之一。

当您调用时,async DoJobAsync()不要忘记为.ConfigureAwait(false)不需要合并回 UI 线程上下文的非 UI 应用程序获得更好的性能。

我没有提到对保持代码整洁有很大帮助的好语法。

微软软件定义网络