c# - 异步编程、线程和效率

标签 c# asp.net async-await

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

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

我错过了什么吗?我的假设错了吗?请解释。

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

最佳答案

Why is this more efficient than just sleeping the thread until the IO operation is finished?

将线程视为 worker 。 worker 很贵。你想付钱给 worker sleep 吗?不。你想付钱让 worker 做尽可能多的工作;如果他们被阻塞,您可以让他们处理其他事情,而不是 sleep ,直到阻塞被清除。

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

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

After all, when the thread is returned to the thread pool, its stack needs to be kept in memory to finish the original request.

我无法理解这句话的正反面。当线程回到池中时,它的堆栈指针又回到底部。

My assumption is that we cannot reuse the memory allocated to the thread unless we copy used stack memory somewhere else and copying data may introduce additional overhead that may not be justified.

我开始理解它了。你的心智模型是:

  • stack是continuation(我们接下来要做什么?)和activation(这个方法activation中locals的值是什么?)的具体化
  • 等待完成的任务必须在工作流程中它们离开的地方继续,本地人不变
  • 因此,继续/激活信息——堆栈——必须复制到某处或某处。

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

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

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

What happens to the stack memory used by the request when the thread is returned to the pool?

只要线程继续存在,为线程堆栈保留的百万字节虚拟内存就会保留。意识到这一点很重要!这就是线程在 .NET 中如此昂贵的原因之一。就操作系统内存管理器而言,这 1MB 的虚拟内存已 100% 使用,无论堆栈中有多少已被压入。这 1MB 中的大部分在其生命周期的大部分时间里都是垃圾。

当线程返回池时,指向该堆栈高水位线的指针被移回开头,超出它的所有内容现在都是垃圾。

If we cannot reuse the memory, then what is the point of reusing the thread?

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

If so, does moving memory around and reusing unused stack memory improve overall efficiency?

我没有理解这个问题的主旨;我怀疑这是基于错误的前提。你能改一下吗?

关于c# - 异步编程、线程和效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41173279/

相关文章:

c# - Entity Framework 代码优先 CTP4 默认列值?

javascript - 是否有任何通用功能可以添加到所有类型的网络浏览器的收藏夹和主页?

javascript - Protractor 和异步/等待

c# - 您将如何正确地异步返回对象集合?

c# - ASP.Net 中的单元测试 ProfileBase

javascript - 如何从异步调用返回错误

c# - WebBrowser.Navigate();如何传递 ntlm 凭据

c# - 这个 Regex 是否应该做我期望的事情,即匹配 "A1:B10,C3,D4:E1000"?

c# - Wpf 文本选取框位于其他元素后面

css - 在 Bootstrap 中使用 RTL 布局时,Asp.net 文本框光标令人困惑