异步的回调
通过以上介绍,我们一定要记住一个知识点:异步需要回调机制。异步操作之所以能在执行结果完成之后继续执行下面程序完全归功于回调,这也是所有异步场景的核心所在,前到js的异步回调,后到cpu内核空间copy数据到用户空间完成通知 等等异步场景,回调无处不在。说道回调大部分语言都是注册一个回调函数,比如js会把回调的方法注册到执行的队列,c#会把回调注册到IOCP。这里延伸一下,在很多系统里,很多IO网络模型其实是属于同步范畴的,比如多路复用技术,真正异步非阻塞的推荐windows下的IOCP。
作者:架构师
链接:https://www.zhihu.com/question/408359907/answer/1369927257
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
真异步是没有专用线程等待的。
在整个过程中,一个关键的结论是没有线程是专门用于运行任务的。虽然工作是在某些上下文中执行的(也就是说,操作系统必须将数据传递给设备驱动程序并响应中断),但是没有专门用于等待来自请求的数据返回的线程。这允许系统处理更大数量的工作,而不是等待一些I/O调用完成。
原理解析
回忆《计算机组成原理》《操作系统》等课程里,我们知道从硬件层面上而言,目前的几乎所有的外部设备的IO都是异步操作,因为IO设备相对于CPU来说非常慢,工业界不会傻到让CPU一直干等着。一般来讲,CPU将数据先提供给IO设备然后去干别的事情,IO设备在处理完毕后向CPU发送中断请求,CPU再回来处理后事;这个特征在高级语言的层面上也有保留,不同语言中的个异步方法,通常都包含开始、回调这对双子星。
因此,高级语言层面上的异步IO操作正是贴合具体硬件实现的、高效率的IO,一个线程只需要处理启动、回调这两条工作就可以完成一个IO操作;而同步IO操作是为了提供更简单的调用而提供的(想想,如果简单的一个Hello World也需要用异步回调那么将会非常不友好),作为代价,一个线程在启动和回调之间处于等待的阻塞状态。
消息模型是如何解决同步IO必须等待IO操作这一问题的呢?当遇到IO操作时,代码只负责发出IO请求,不等待IO结果,然后直接结束本轮消息处理.
进入下一轮消息处理过程。当IO操作完成后,将收到一条“IO完成”的消息,处理该消息时就可以直接获取IO操作结果。
(处理过程:会开启监视线程负责处理-> 任务的启动与运行完成后的回调【一个线程可以处理多个请求】)
在“发出IO请求”到收到“IO完成”的这段时间里,同步IO模型下,主线程只能挂起,但异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息。
.这样,在异步IO模型下,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。对于大多数IO密集型的应用程序,使用异步IO将大大提升系统的多任务处理能力。
总结
虽然我们可以包装同步IO方法使其同样可以通过await
来达到不阻塞调用的效果,但是它和原生的异步IO方法具有本质上的区别,在于同步IO终究还是会阻塞线程池里的线程,而异步IO则不会。从提高吞吐量的角度而言,await
一个包装起来的同步IO并不能享受本质的提升。
C# 异步陷阱(Task.Run)
看一个简单的例子
public async Task<string> MyTaskAsync(Uri address)
{
return await Task.Run(() =>
{
using (var client = new WebClient())
{
return client.DownloadString(address);
}
});
}
乍一眼看这个函数似乎很正常,用一个Task包裹一个耗时的WebClient.DownloadString函数。但是我们需要问一下自己:
WebClient.DownloadString 是CPU密集型,还是IO密集型
在Task.Run所在的托管线程里,是阻塞的还是非阻塞的
答案很明确:IO密集型,并且仍然是阻塞的,那么这意味着什么:我们只是换了一个地方阻塞,也就是类似于拆东墙补西墙,这样的代码确实可以使UI不再卡顿,但实际上对于性能的可拓展并没有什么好处。
public async Task<string> MyTaskAsync(Uri address)
{
using (var client = new HttpClient())
{
return await client.GetStringAsync(address);
}
}
异步底层原理(真异步是贴合具体底层硬件实现的)
https://blog.csdn.net/WuLex/article/details/123334063
异步分为两类 compute-base 和 IO-base。compute-base就是计算密集型,只要是.net基本类库提供的异步函数基本都是IO-base Task(微软官方文档是这样要求)。其实这样要求是有道理的:对于compute-base异步,比较容易封装;再者,这样的异步是不能大规模的并发的。如果16个线程cpu,同时并发16个这样的异步操作就是上限了;如果再多,反而会有害!
https://www.jb51.net/article/207090.htm
了解 .NET 的默认 TaskScheduler 和线程池(ThreadPool)设置,避免让 Task.Run 的性能急剧降低
https://blog.walterlv.com/post/default-task-scheduler-and-thread-pool.html
ASP.NET Core 最佳做法 | Microsoft Learn
微软文档
多线程简便写法
await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s))));
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"ConnectionStrings": {
"DbConnection": ""
},
"CBI360_JST": "server=192.168.11.238;user id=cbi360-jst;password=cbi#^)jst2018;database=CBI360_JST_CJR",
"Mongodb": "mongodb://root:[email protected]:3717,dds-bp172db9e1d2e7d42944.mongodb.rds.aliyuncs.com:3717/admin?replicaSet=mgset-5242623",
"SplitCount": 4,
"SplitIndex": 1,
"IsEndProcedure": false,
"MinId": 24,
"MaxId": 22371145
}
//20292168 20295017 20297867
server=121.43.37.188;user id=cbi360;password=Icb)^#3g4B!;database=HttpReports;
标签:异步,Task,Core,线程,IO,Net,回调,CPU From: https://www.cnblogs.com/kkbk/p/17702096.html