async await 理解
感谢这位大佬的讲解
async await 异步,感觉是充分的利用了线程数。异步是指不是同一个线程的意思。
作者:知乎用户
链接:https://www.zhihu.com/question/439143561/answer/1678582955
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
异步方法和同步方法哪里不同呢?
首先异步 != 多线程 , 这是两个层次的概念。多线程是实现异步的方式之一,单线程也一样可以实现异步。比如node.js。
C#中更重要的是await,而不是async 。 async只是给编译器的提示标记。所以我们具体看看同步/异步函数内部的区别。
public ActionResult PushFileData(FileData file){
db.Save(file);
Log("done");
}
public async Task<ActionResult> PushFileData(FileData file){
await db.SaveAsync(file);
Log("done");
}
同步:
线程A开始执行代码,之后同步执行db.Save方法。 同步执行意味着,将数据发送到数据库,再到数返回写入成功,这一段时间,线程A是被阻塞的。也就是白白浪费了CPU的时间片。以及线程池中的一个线程对象。
Save方法执行完之后仍然由线程A执行Log方法。
异步:
线程A开始执行代码,之后异步执行SaveAsync方法。 异步执行意味着,将数据发送并等待的这段时间,线程A被放回了线程池,换言之,没有占用任何线程。既然没有占用任何线程也就不存在占用CPU时间片。
之后等到SaveAsync方法执行完毕后,又从线程池中获取一个线程(可能是A,也可能是其他线程),在执行Log方法。
所以,上面的异步代码也等价于
public Task<ActionResult> PushFileData(FileData file){
return Task.Run(() => db.SaveAsync(file))
.ContinueWith(lastTask => Log("done"));
}
//就和ES6的 .then()方法一样
那么你可能又会问,你噼里啪啦说了这么多,同步方法和异步方法对于http://APS.NET CORE好像并没有什么用啊? 对于GUI程序起码还能让主界面不至于卡死。
经过上面的一番分析之后,我们知道http://ASP.NET CORE在执行控制器方法的时候同样依赖线程池。
当有大量并发的请求时。比如10个吧,而你线程池的线程只有5个可用线程。
那么你同步方法就会在同一时刻占用5个线程处理5个请求,剩下的5个请求就排队等待了。
异步方法用于只执行本机的代码,不会阻塞等待网络IO,提前被放回线程池,那么原本剩下的5个请求就能被更快的处理啦。
有了以上的认知我们来看一下题主的问题。
疑问:对于同步方法,每个请求都是使用同个线程吗?如客户A请求同步Action,还未执行完毕时,客户B请求会阻塞。
无论是同步方法还是异步方法,都会从线程池获取线程获取一个线程执行。
所以,“ 使用同个线程?” ,答案是不一定。
- 如果线程A执行完被放回了线程池,下一次请求过来仍然获取的A线程,那么两次使用的是同一个线程。
- 如果线程A正在使用,则线程池中不存在A线程,可能获取的B线程,则是不同线程。
- 如果线程A执行完被放回了线程池,下一次请求过来获取的是B线程,则是不同线程。
“还未执行完毕时,客户B请求会阻塞?”
- 如果线程池中还有剩余线程时,那么不会阻塞。
- 如果线程池中没有剩余线程时,那么会尝试创建一个新的线程。有两个阈值,一个是单位时间内创建的线程数,另一个是线程池中的总数。
- 如果没有到阈值,则创建线程。不会阻塞。
- 如果到达阈值,阻塞等待。
对于异步方法,每个请求都是从线程池拿空闲线程出来执行方法?也就是客户A和客户B请求方法,都是在不同子线程里分别执行的