我的问题不仅适用于C#和Asp.net,但对我来说更容易提出更具体的问题。
当Asp.net请求等待异步IO操作时,线程基本上会进入线程池,由其他请求重用。为什么这比在IO操作完成之前只休眠线程更有效?毕竟,当线程返回到线程池时,需要将其堆栈保存在内存中以完成原始请求。我的假设是,除非我们将已使用的堆栈内存复制到其他地方,否则不能重用分配给线程的内存,并且复制数据可能会带来额外的开销,这可能是不合理的。
我是不是遗漏了什么?我的假设是错的吗?请解释一下。
编辑: jlew指出的答案缺少一点。当线程返回到池时,请求使用的堆栈内存会发生什么变化?如果我们不能重用内存,那么重用线程有什么意义呢?如果我们想重用堆栈中没有使用的部分,那么我们必须移动一些内存。如果是这样的话,移动内存和重用未使用的堆栈内存会提高整体效率吗?
发布于 2016-12-15 21:51:28
为什么这比在IO操作完成之前只休眠线程更有效?
把线程看作是工人。工人很贵。你想付钱给工人睡觉吗?不是的。你想让工人做尽可能多的工作;如果他们被阻止了,你就让他们做其他的事情,而不是睡觉,直到堵塞被清除为止。
线程太贵了。如果线是便宜的,那么肯定,我们可以做很多。您只对昂贵的资源使用池策略。
线程的开销主要有两种:您提到堆栈的大小,即每个线程保留1MB的虚拟内存。但是,在OS级别上也有很大的成本,这并不是针对有数千个线程的场景进行优化的。确定接下来要运行的线程,上下文切换到它,然后从它切换,所有这些都有非零的成本,并且随着线程数量的增加而增加。理想情况下,您需要n个独立的线程在n处理器机器上,不要多,不要少。
毕竟,当线程返回到线程池时,需要将其堆栈保存在内存中以完成原始请求。
这句话我不能说正反两面。当线程返回到池时,它的堆栈指针回到底部。
我的假设是,除非我们将已使用的堆栈内存复制到其他地方,否则不能重用分配给线程的内存,并且复制数据可能会带来额外的开销,这可能是不合理的。
我开始明白了。你的心理模型是:
这种心理模型看似合理,但却是错误的。异步工作流构建状态机并将延续绑定为该机器中的状态,然后在任务中存储对状态机的引用。激活信息从堆栈槽提升到闭包类的字段中。这将从堆栈中获取延续/激活信息并进入堆中。
现在,请记住,任务的继续并不包含堆栈上的所有信息;它不是真正的延续,因为它捕获了当前方法完成时发生的事情。它捕获当前等待的任务完成时发生的情况,这就足够了。
记住,当“我下一步要做什么?”时,堆栈只能被用作延续的具体化。工作流在逻辑上是一个堆栈--接下来要做的就是堆栈顶部的事情。但是异步工作流形成了一个依赖树,连接点正在等待;它一开始并不是一个堆栈,因此将连续表示为堆栈是不可能的。
当线程返回到池时,请求使用的堆栈内存会发生什么变化?
只要线程继续存在,为线程堆栈保留的百万字节虚拟内存将保持保留状态。这一点很重要!这就是为什么线程在.NET中如此昂贵的原因之一。对于操作系统内存管理器来说,1MB的虚拟内存是100%的使用,不管这个堆栈被推到了多少。这1MB中的大部分在其生命周期中大部分都是垃圾。
当线程返回到池时,指向该堆栈的高水标记的指针被移回起始位置,之后的所有内容现在都是垃圾。
如果我们不能重用内存,那么重用线程有什么意义呢?
我们可以并且确实重用内存;堆栈指针被重置。
如果是这样的话,移动内存和重用未使用的堆栈内存会提高整体效率吗?
我没有遵循这个问题的主旨,我怀疑这是基于错误的前提。你能改个说法吗?
https://stackoverflow.com/questions/41173279
复制相似问题