Async/Await - 性能开销和其他陷阱

  • Roopesh Shenoy
  • 姚琪琳

2013 年 7 月 25 日

话题:语言 & 开发

Async/Await 是 C# 5 引入的最强大的语言特性。但有哪些陷阱需要注意呢?与这两个关键字相关的开销又有哪些?

MSDN 上的一篇文章“异步编程最佳实践”强调了以下几点:

  • 选择异步 Task 方法而非异步 void 方法,事件处理程序除外。它们采用不同的错误处理语义。Marker Metro的 Keith Patton详细解释了这一点
  • 避免混合使用阻塞和非阻塞代码,这可能会导致死锁、更加复杂的错误处理以及上下文线程的意外阻塞。
  • 如果后续代码不需要使用原始上下文,就用 ConfigureAwait(false) 来获得更好的性能。这会使后续代码运行在线程池上下文中。如果你不得不混合编写同步和异步的代码,它还可以避免死锁。注意,在服务端使用时会有略微不同的考虑

RedGate 的软件工程师Chris Hurley解释了 async-await 的 CPU 开销,并通过一小段示例代码进行了演示:

  • 用 async 关键字调用一个方法,会创建一个状态机,并构建一个 Task 对象来包含要执行的工作,以及获取执行上下文和同步上下文。
  • 在该展示示例中,第一次调用共运行了 963 个框架方法,来初始化相对简单的 async 方法。
  • 上下文将被缓存,因此后续调用会减少很多开销。
  • 对于在很短的时间内(1 毫秒)就能同步运行完的方法,异步开销将阻塞调用线程更长的时间。在示例中,调用线程解除阻塞差不多要用 45 毫秒的时间。即使在循环中,后续调用会大幅减少开销的情况下,调用线程也看不到任何性能优势。
  • 结论——避免对(耗时)短的方法使用 async/await,避免在小循环内使用 await 语句(可以将整个循环放到一个 async 方法内)。

我们之前已经介绍过一些在使用这两个关键字时会遇到的其他常见陷阱

查看英文原文:Async/Await – Performance Overheads and Other Pitfalls

语言 & 开发