我正在深入研究内存模型,但在理解进程中有多少个堆时遇到了困难。
假设我们有一个包含 5 个线程的进程,那么我说这意味着我们有 5 个栈和 1 个堆,这是正确的吗?
如果是这样,那么线程能够访问彼此的栈吗(或者说这恰好是它们具有单独的栈的原因,以防止出现数据损坏),如果只有 1 个堆,显然它们都可以访问该堆,因此需要使用锁来支持多个线程?我的理解是否正确?
我正在深入研究内存模型,但在理解进程中有多少个堆时遇到了困难。
假设我们有一个包含 5 个线程的进程,那么我说这意味着我们有 5 个栈和 1 个堆,这是正确的吗?
如果是这样,那么线程能够访问彼此的栈吗(或者说这恰好是它们具有单独的栈的原因,以防止出现数据损坏),如果只有 1 个堆,显然它们都可以访问该堆,因此需要使用锁来支持多个线程?我的理解是否正确?
是的,每个线程都有自己的堆栈。这是一个硬性要求,堆栈跟踪方法在完成后返回的位置,它存储返回地址。由于每个线程执行自己的代码,它们需要自己的堆栈。局部变量和方法参数也存储在其中,使它们(通常)是线程安全的。
堆的数量是一个更复杂的细节。你将垃圾收集堆计入1中。从实现角度来看,三代堆加上大对象堆是逻辑上不同的堆,加起来是四个。当你分配太多时,这个实现细节开始变得重要。
在托管代码中不能完全忽略的另一个是存储静态变量的堆。它与AppDomain关联,静态变量的生存期与AppDomain一样长。在.NET文献中通常称为“加载器堆”。它实际上由三个堆(高频、低频和存根堆)组成,jitted代码和类型数据也存储在其中,但这已经涉及到很细节的问题。
忽略列表更下面的是本机代码使用的堆。其中两个可以从Marshal类中轻松地看到。有一个默认的进程堆,Windows从中分配,Marshal.AllocHGlobal()也是如此。还有一个单独的堆,COM存储数据,Marshal.AllocCoTaskMem()从其中分配。最后,任何你与之进行互操作的本机代码都将拥有其自己的堆来支持其运行时。这种代码使用的堆的数量仅受到加载到进程中的本机DLL数量的限制。所有这些堆都存在,但你几乎从不直接处理它们。
所以,最少需要10个堆。
简而言之,是的。
一个进程中的所有线程共享相同的堆,因此它们可以交换数据。每个线程都有自己的栈,与该线程上当前代码执行相关。
关于多线程的非常好的资源在这里:http://www.albahari.com/threading/
一个线程类似于您的应用程序运行的操作系统进程。就像进程在计算机上并行运行一样,线程在单个进程内并行运行。进程彼此完全隔离;线程只有有限的隔离程度。特别地,线程与在同一应用程序中运行的其他线程共享(堆)内存。这部分是多线程有用的原因之一:例如,一个线程可以在后台获取数据,而另一个线程可以在数据到达时显示数据。
unsafe
块之外,这些情况不会发生,因为CLR管理了堆栈。
PostThreadMessage
向已经运行的线程发送消息。 - Hristo Iliev