看到这个问题和答案;
Why use async controllers, when IIS already handles the request concurrency?
好的,一个线程比async / await构建消耗更多的资源,但为什么?核心区别是什么?你仍然需要记住所有州等,不是吗?
为什么线程池会被限制,但是你可以有更多空闲的async / await构造吗?
是因为async / await了解您的应用程序的更多信息吗?
答案 0 :(得分:3)
创建一个新线程为这个拥有其资源的线程分配一个独立的内存区域,主要是它在Windows中占用1MB
内存的调用堆栈。
因此,如果您有1000个空闲线程,那么至少使用1GB
内存无效。
异步操作的状态也占用了内存,但它只是该操作所需的实际大小以及编译器生成的状态机,它保存在堆上。
此外,使用多个线程并阻止它们还有另一个成本(IMO更大)。当线程被阻塞时,它被取出CPU并与另一个线程切换(即上下文切换)。这意味着您的线程在被阻止时不会以最佳方式使用其时间片。 更高的上下文切换速率意味着您的机器会更多地进行上下文切换,并减少各个线程的实际工作量。
使用async-await可以使用所有给定的时间片,因为线程而不是阻塞会返回到线程池,并在异步操作同时继续时执行另一个任务。
因此,总而言之,async await释放的资源是CPU和内存,允许您的服务器使用相同数量的资源或相同数量的请求同时处理更多请求,而资源更少。
答案 1 :(得分:3)
好吧,让我们想象一下网络服务器。他大部分时间都在等待。它通常不受CPU限制,但更多的I / O绑定。它等待网络I / O,磁盘I / O等。每次等待之后,他都有一些东西(通常很短暂),然后他所做的就是再次等待。现在,有趣的部分是在他等待时发生的事情。在最“琐碎”的情况下(当然绝对不是生产),你会创建一个线程来处理你拥有的每个套接字。
现在,每个线程都有自己的成本。一些句柄,1MB的堆栈空间......当然,并非所有这些线程都可以在同一时间运行 - 因此OS调度程序需要处理它并选择正确的线程每次运行(这意味着很多上下文交换)。它适用于1个客户。它适用于10个客户。但是,让我们同时想象10,000个客户。 10,000个线程意味着10GB的内存。这不仅仅是世界上普通的Web服务器。
所有这些资源,都是因为您为用户专门设置了一个线程。但是,这些线程中的大部分都没有做任何事!他们只是等待一些事情发生。操作系统具有异步IO的API,允许您对IO操作完成后将要执行的操作进行排队,而无需专用线程等待它。
如果你使用async / await,你可以编写容易使用较少线程的应用程序,并且每个线程将被更多地利用 - 减少“无所事事”的时间。
async / await不是唯一的方法。您可以在引入async / await之前完成此操作。但是,async / await允许你编写非常易读且易于编写的代码,并且看起来几乎只在单个线程上运行(而不是像以前那样在很多回调和代理中移动)。
通过结合async / await的简单语法和操作系统的某些功能(如异步I / O(通过使用IO完成端口)),您可以编写更具伸缩性的代码,而不会失去可读性。
另一个着名的示例是WPF / WinForms。你有UI线程,他所做的只是处理事件,而且通常没什么特别的事情要做。但是,您无法阻止它或GUI将挂起而用户不会喜欢它。通过使用async / await并将每个“硬”工作拆分为简短操作,您可以实现负责任的UI和可读代码。如果您必须访问数据库以执行查询,您将从UI线程启动异步操作,然后您将“等待”它直到它结束并且您有可以在UI线程中处理的结果(因为例如,您需要向用户显示它们。您之前可以这样做,但使用async / await可以使其更具可读性。
希望它有所帮助。
答案 2 :(得分:0)
这里要认识到的重要一点是,被阻塞的线程在被解除阻塞之前不能用于执行任何其他工作。遇到await的线程可以自由返回到线程池并获取其他工作,直到等待的值可用为止。
答案 3 :(得分:0)
当您调用同步I / O方法时,执行代码的线程将被阻止,等待I / O完成。要处理1000个并发请求,您将需要1000个线程。
调用异步I / O方法时,不会阻止该线程。它初始化I / O操作并可以处理其他事情。它可以是您方法的其余部分(如果您没有await
),或者如果您await
I / O方法可能是其他请求。线程池不需要为新请求创建新线程,因为可以最佳地使用所有线程并使CPU保持忙碌。
异步I / O操作实际上是在OS level异步实现的。