我想了解当我在.NET中生成一个新线程时,场景背后究竟发生了什么,如下所示:
Thread t = new Thread(DoWork); //I am not interested in DoWork per se
t.Start();
1。在CLR和Windows内核中创建了哪些与线程相关的对象?
2.为什么需要这些物品?
3.在x86,x64 Windows上分配了多少托管/非托管内存(堆和堆栈)?
更新
我正在寻找托管线程对象这样的对象,我假设它是 t ,但也许是其他一些额外的托管对象; 内核线程对象,用户线程环境块等。
非常感谢!
答案 0 :(得分:9)
我不确定.NET部分是如何工作的,但是如果运行时决定用OS创建一个真正的线程,它最终会调用kernel32.dll中的Win32 API CreateThread,可能来自mscorlib.ni.dll
默认情况下,新线程为堆栈获取1MB的虚拟地址,并根据需要提交。这可以通过maxStackSize
parameter来控制。主线程的堆栈大小来自可执行文件本身的参数。
在进程的地址空间中,将分配TEB(线程环境块)(see also)。顺便提一下,x86上的FS寄存器指向线程本地存储和结构化异常处理(SEH)之类的东西。可能还有其他由Win32分配的未记录的内容。
在创建Win32线程时,会联系Win32服务器进程(csrss.exe)。您可以看到csrss具有对Process Explorer中的所有Win32进程和线程开放的句柄,用于某种簿记。
在进程中加载的DLL将被通知新线程,并可能分配自己的内存来跟踪线程。
内核将从内核非页面缓冲池创建ETHREAD
[layout](从KTHREAD派生)对象,以跟踪线程的状态。还将分配一个内核堆栈(x86默认为12k),可以将其分页(除非线程处于内核模式等待状态)。
线程是操作系统提供的最小的抢先调度单元,并且有很多连接到它们的上下文。许多不同的组件需要为每个线程提供单独的上下文,因为系统服务需要能够同时处理多个执行不同操作的线程。
某些服务要求您明确地向它们声明新线程,但大多数服务需要自动使用新线程。有时这意味着在线程启动时正确分配空间。当线程使用其他服务时,用于跟踪线程的内存量会随着这些服务为线程设置自己的上下文而增加。
很难说为线程分配了多少内存,因为它分布在多个地址空间和堆上。它将在Windows版本,已安装组件和当前加载到进程中的内容之间有所不同。
最大的成本通常被认为是新线程默认使用的1MB地址空间,但即使是这个限制也可以在一个进程中使用数百个,而不会耗尽空间。
如果设计使用的OS线程数多于系统中CPU的数量,则应对其进行检查。具有线程池和轻量级线程的工作队列以及使用光纤或其他库实现的用户模式调度应该能够处理多线程处理而不需要过多的OS线程,从而使线程的内存成本变得不重要。
答案 1 :(得分:2)
所以这是一个非常复杂的问题,对“x”的回答并不是很好。
Thread
对象的一部分。如果你深入研究IL,你会看到很多内部调用实际执行。 ThreadPool
用法可以大大降低每次创建它们的成本。“控件线程的逻辑抽象由类库中的System.Threading.Thread
对象的实例捕获。” http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
因此,EMCA标准真的没有说明这个话题。但幸运的是我们有......
“因为CLR线程对象是每个光纤,所以挂起它的任何信息也是每个光纤.Thread.ManagedThreadId返回一个与CLR线程一起流动的稳定ID。它不依赖于它的身份物理操作系统线程,意味着使用它意味着没有形式的亲和力。在同一个线程上运行的不同光纤返回不同的ID。“来自Joe Duffy http://www.bluebytesoftware.com/blog/2006/11/10/FibersAndTheCLR.aspx
答案 2 :(得分:1)
看here;托管(即CLR)原语和非托管(即NT内核)原语之间存在映射,可以回答大部分问题。