什么是C#线程类型?

Sam*_*ami 10 .net c# winapi multithreading

在三种类型的线程(内核级别,用户级别和混合)之间,C#(或更一般地说是.NET)使用哪种类型?

PaR*_*RaJ 8

内核线程(1:1)

术语"内核线程"可用于指代完全在内核空间中运行的实际线程,或者它可以引用内核调度的用户空间线程.术语"内核支持"线程意味着后者,线程在用户空间中运行但由内核促进,这通常意味着内核对它们进行调度.

内核线程具有特权,可以访问用户模式线程的限制.看看维基百科上的" Ring(计算机安全) ".在Windows上,用户模式对应于Ring 3,而内核模式对应于Ring 0.

用户级线程(N:1)

"用户级线程"通常表示用户空间可见的线程.也就是说,当您调用线程标准的"创建线程"函数时创建的内容.通常,术语"用户级线程"用于表示由应用程序代码创建的线程,而不管系统如何实现它.它可能是纯粹的用户空间线程,几乎没有内核支持,也可能是内核调度的线程.

混合线程(M:N)[WikiPedia]

M:N将一些M个应用程序线程映射到N个内核实体或"虚拟处理器"上.这是内核级("1:1")和用户级("N:1")线程之间的折衷.通常,"M:N"线程系统的实现比内核或用户线程更复杂,因为需要更改内核和用户空间代码.在M:N实现中,线程库负责在可用的可调度实体上调度用户线程; 这使得线程的上下文切换非常快,因为它避免了系统调用.

.Net线程是用户级线程,它使用Win32 API并将其包装为一个很好的框架!


更多细节可以找到:


ole*_*sii 6

这不是C#本身,而是运行时 - CLR.

.Net可以被认为支持两种模式:

  • 仅限用户登陆(或Windows UMS).在此模式下,一个用户登陆线程可以执行上下文切换到另一个用户模式线程,而无需实际更改内核线程.此模式增加了大量编码开销,但在内核级上下文切换中节省了宝贵的10-20微秒.
  • 混合模型(迄今为止最受欢迎).在此模型中,托管代码需要访问内核,例如读取文件.通过一组API,代码执行从用户域.NET调用,到Windows API,到内核,驱动程序,HAL层和物理驱动器.然后通过调用堆栈获取数据.如果线程调度程序启动了上下文切换,则会更改用户线程和内核线程.

不允许直接内核模式线程,因为托管代码没有在内核模式下运行的权限.它只能调用Windows API,后者又会切换到内核模式.


如果使用Thread,Task,ThreadPool- CLR使用混合模式.使用混合模型是因为CLR创建托管对象来表示该类.任何托管代码都在用户域中运行.但是,每个未创建为fibber的线程(见下文),无论是否在线程池中,都有底层内核数据结构.内核数据结构用于保存线程的内核状态 - 内核线程id,创建和退出时间,进程ID,启动线程地址,安全访问令牌,十几个定时器,CPU寄存器,内核堆栈等.所有这些都需要在更新期间更新一个上下文切换,但我们只需要大约10-20*10 ^ -6秒的命中.

如果您使用围绕Ums C++调用集的托管包装器(如CreateUmsThreadContext,UmsThreadYield和许多其他人) - CLR尝试使用UMS模式.但这仅适用于您专门和手动操作的少数线程.您的应用程序仍将使用混合模型,只需在用户模式下手动选择一些手动选择的线程(纤维).但即使在这种模式下,OS任务调度程序在某些时候也会启动内核线程切换,因此您永远不会执行UMS调度线程.

为了完整,

  • 内核模式调度是一种非常低级别的机制,只能从内核访问.这意味着你只能使用它,如果你开发操作系统内部或某种驱动程序.这种调度很难使用,任何错误和整个操作系统都被冻结/卡住/崩溃/ BSOD或转换到任何其他不可预测的状态.内核级线程需要大量的开发和测试工作.例如,Windows任务调度程序或内存管理服务使用内核模式调度.您能想象一下编写这些组件所花费的时间,以便它们在高度多线程的环境中保持稳定吗?