我正在寻找关于异步/等待有意义的“工作负载阈值”(例如释放IO完成端口并避免线程饥饿)与工作单元执行过于简单/便宜之间的一些指导,所以同步执行是一个更好的选择。
换句话说,当与相对快速/资源廉价的工作单元结合使用时,async / await的使用会导致性能下降,并且只是同步执行工作会是首选方法吗?
示例(所有在一个方法中,都与await异步):
ReadAsStreamAsync
CopyToAsync
FlushAsync
答案 0 :(得分:10)
我建议你不是从时间角度来看,而是从I / O-vs-CPU的角度来看待它。
CPU绑定方法自然是同步的; I / O绑定方法自然是异步的。
我假设你的环境是服务器端的,基于你的例子:
所有这些都是自然异步操作,因此它们都应该异步实现。 CPU比内存快得多,这比网络或磁盘I / O快得多。因此,如果同步实现自然异步方法,将阻塞线程。这不是世界末日,但线程池必须补偿它是否被阻塞太久,而唯一一次“保存”是线程切换时间,这将是数量级更短比任何网络或磁盘I / O都可能。
要考虑的另一件事是不可预测的延迟,这通常是由于资源争用而发生的。如果另一个进程同时写入db,该怎么办?如果文件上传时出现路由打嗝,需要重新传输数据包怎么办?如果在您尝试写入输出文件时磁盘进行碎片整理会怎样?异步操作倾向于像这样不可预测,并且async
代码确保您不会比预期更长时间地阻塞线程。
总之:对同步(CPU绑定)工作使用同步代码,对异步(I / O绑定)工作使用异步代码。
答案 1 :(得分:3)
首先,您应该知道仅仅因为使用await
关键字异步调用方法,这并不意味着该方法无法同步运行。或者确切地说:此类方法通常会返回Task
,Task<T>
或IAsyncOperation<TResult>
(在Windows运行时中),并且该方法返回时很可能完成该任务。在这种情况下,开销非常小,因为执行的线程将继续运行。
至于阈值本身,这取决于您想要做什么以及您在哪个环境中运行。这是UI应用程序还是服务器应用程序?您想异步运行以释放UI,还是更好地(即更具可伸缩性)使用服务器线程?
对于Windows运行时API,Microsoft使用了50毫秒的阈值,这意味着任何可能需要超过50毫秒才能执行的方法仅以异步形式提供。这背后的逻辑非常简单:这样做可以使UI线程执行长程序,并且永远不会被阻塞超过50毫秒。换句话说,UI线程可以运行其他有用的代码,例如,渲染帧,每秒20次或更多。
Charles Petzold写道a good article about it on his blog。
对于服务器场景,只要通过释放线程完成的工作不仅仅是释放线程所需的工作,就可以异步运行。根据我的经验,几乎所有IO都是如此。当然有一些看起来像IO的例外,但它实际上是在读取或写入内存缓冲区,但在这些情况下,该方法返回的任务将同步完成。