Server-side asynchronous code purpose is completely different from asynchronous UI code.
Asynchronous UI code makes UI more responsive (especially when multiple CPU cores are available), it allows multiple UI tasks to run in parallel which improves UI user experience.
The purpose of server-side asynchronous code on the other hand is to minimise the resources necessary to serve multiple clients simultaneously. In fact it is beneficial even if there is only one CPU core or a single-threaded event loop like in Node.js. And it all boils down to a simple concept of
Asynchronous IO.
同步IO和异步IO之间的区别在于,对于前者,初始化IO操作的线程将暂停,直到IO操作完成(例如,直到执行DB请求或读取磁盘上的文件).一旦IO操作完成,同一个线程就会被取消暂停,以处理其结果.注意:尽管暂停时线程很可能没有使用任何CPU资源(它可能被线程调度程序置于睡眠状态),但它的资源仍然与这个特定的IO操作有关,并且在硬件执行IO时浪费了大量资源.有效地使用同步IO,每个当前正在处理的客户端请求至少需要一个线程,即使这些线程中的大多数可能正在Hibernate ,等待IO操作完成.在里面NET每个线程至少分配了1MB的堆栈,因此,如果服务器当前正在处理1000个请求,那么只为线程堆栈分配了近1GB的内存,再加上线程调度程序的额外负担,以及CPU花费在上下文切换上的时间:线程越多,系统的整体性能越低.分配的内存越多,内存/CPU缓存的使用效率也越低.
异步IO效率更高,因为工作线程只初始化一个IO操作,而不是等待它完成,它会立即切换到另一个有用的任务(例如,继续另一个客户端的请求处理),当硬件完成IO操作时,结果的处理会在任何可用的工作线程上恢复.因此,根据等待硬件完成IO的总时间与执行CPU任务(例如,将IO操作结果序列化为JSON)的时间之间的比率,这种方法可以使用less threads来服务相同数量的同时客户端请求:如果,90%的时间都花在IO上,我们可能只使用less threads个线程来处理同样的less threads0个并发请求.服务器端代码越是受IO约束而不是CPU约束,它就越能使用给定的资源量(CPU和内存)处理并发的客户端请求.
异步代码的缺点是什么?主要是,它通常比同步更难写.异步代码使用回调来恢复操作,因此程序员需要将委托(延续)传递给IO方法,而不是简单的线性代码.IO操作完成后(可能在不同的线程上),系统会调用该方法.然而,拥有async/await
个功能的现代C#使这项任务变得不那么复杂,甚至使异步代码看起来几乎像是同步的.唯一需要记住的是:异步代码只有在"一路向下"异步时才能工作:从初始HTTP请求处理到DB请求调用的调用堆栈中的某个地方,即使是单个Task.Wait或Task.Result,也会使整个代码同步,从而迫使当前工作线程等待Wait
次调用完成,以达到预期目的.注:await
in C#代码实际上并不等待调用的结果,而是由编译器转换为ContinueWith,即转换为延续回调,虽然实际上是more complicated than that位,但幸运的是程序员隐藏了复杂性,因此现在编写高效的异步代码是一项相对简单的任务.