首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >异步编程、线程和效率

异步编程、线程和效率
EN

Stack Overflow用户
提问于 2016-12-15 21:02:23
回答 1查看 315关注 0票数 3

我的问题不仅适用于C#和Asp.net,但对我来说更容易提出更具体的问题。

当Asp.net请求等待异步IO操作时,线程基本上会进入线程池,由其他请求重用。为什么这比在IO操作完成之前只休眠线程更有效?毕竟,当线程返回到线程池时,需要将其堆栈保存在内存中以完成原始请求。我的假设是,除非我们将已使用的堆栈内存复制到其他地方,否则不能重用分配给线程的内存,并且复制数据可能会带来额外的开销,这可能是不合理的。

我是不是遗漏了什么?我的假设是错的吗?请解释一下。

编辑: jlew指出的答案缺少一点。当线程返回到池时,请求使用的堆栈内存会发生什么变化?如果我们不能重用内存,那么重用线程有什么意义呢?如果我们想重用堆栈中没有使用的部分,那么我们必须移动一些内存。如果是这样的话,移动内存和重用未使用的堆栈内存会提高整体效率吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-12-15 21:51:28

为什么这比在IO操作完成之前只休眠线程更有效?

把线程看作是工人。工人很贵。你想付钱给工人睡觉吗?不是的。你想让工人做尽可能多的工作;如果他们被阻止了,你就让他们做其他的事情,而不是睡觉,直到堵塞被清除为止。

线程太贵了。如果线是便宜的,那么肯定,我们可以做很多。您只对昂贵的资源使用池策略。

线程的开销主要有两种:您提到堆栈的大小,即每个线程保留1MB的虚拟内存。但是,在OS级别上也有很大的成本,这并不是针对有数千个线程的场景进行优化的。确定接下来要运行的线程,上下文切换到它,然后从它切换,所有这些都有非零的成本,并且随着线程数量的增加而增加。理想情况下,您需要n个独立的线程在n处理器机器上,不要多,不要少。

毕竟,当线程返回到线程池时,需要将其堆栈保存在内存中以完成原始请求。

这句话我不能说正反两面。当线程返回到池时,它的堆栈指针回到底部。

我的假设是,除非我们将已使用的堆栈内存复制到其他地方,否则不能重用分配给线程的内存,并且复制数据可能会带来额外的开销,这可能是不合理的。

我开始明白了。你的心理模型是:

  • 堆栈是延续的具体化(接下来我们要做什么?)以及激活(此方法激活中的本地值是多少?)
  • 等待完成的任务必须在工作流程中停止时继续进行,而局部变量则保持不变。
  • 因此,继续/激活信息--堆栈--必须复制到某个地方,或者什么地方。

这种心理模型看似合理,但却是错误的。异步工作流构建状态机并将延续绑定为该机器中的状态,然后在任务中存储对状态机的引用。激活信息从堆栈槽提升到闭包类的字段中。这将从堆栈中获取延续/激活信息并进入堆中。

现在,请记住,任务的继续并不包含堆栈上的所有信息;它不是真正的延续,因为它捕获了当前方法完成时发生的事情。它捕获当前等待的任务完成时发生的情况,这就足够了。

记住,当“我下一步要做什么?”时,堆栈只能被用作延续的具体化。工作流在逻辑上是一个堆栈--接下来要做的就是堆栈顶部的事情。但是异步工作流形成了一个依赖树,连接点正在等待;它一开始并不是一个堆栈,因此将连续表示为堆栈是不可能的。

当线程返回到池时,请求使用的堆栈内存会发生什么变化?

只要线程继续存在,为线程堆栈保留的百万字节虚拟内存将保持保留状态。这一点很重要!这就是为什么线程在.NET中如此昂贵的原因之一。对于操作系统内存管理器来说,1MB的虚拟内存是100%的使用,不管这个堆栈被推到了多少。这1MB中的大部分在其生命周期中大部分都是垃圾。

当线程返回到池时,指向该堆栈的高水标记的指针被移回起始位置,之后的所有内容现在都是垃圾。

如果我们不能重用内存,那么重用线程有什么意义呢?

我们可以并且确实重用内存;堆栈指针被重置。

如果是这样的话,移动内存和重用未使用的堆栈内存会提高整体效率吗?

我没有遵循这个问题的主旨,我怀疑这是基于错误的前提。你能改个说法吗?

票数 10
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41173279

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档