10

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

4

2 回答 2

8

核心线程 (1 : 1)

术语“内核线程”可以用来指代完全在内核空间中运行的实际线程,也可以指由内核调度的用户空间线程。术语“内核支持”线程是指后者,即在用户空间中运行但由内核促进的线程,这通常意味着内核调度它们。

内核线程具有特权,并且可以访问用户模式线程无法访问的内容。看看维基百科上的“ Ring(计算机安全) ”。在 Windows 上,用户模式对应于 Ring 3,而内核模式对应于 Ring 0。

用户级线程 (N : 1)

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

混合线程(M:N)[维基百科]

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

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


可以找到更多详细信息:

于 2013-02-26T15:58:05.880 回答
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 模式。但这仅适用于您专门手动操作的少数线程。您的应用程序仍将使用混合模型,在用户模式下,将在彼此之间手动生成一些手动选择的线程(纤维)。但即使在这种模式下,操作系统任务调度程序也会在某些时候启动内核线程切换,因此您不会永远被困在执行 UMS 调度线程。

为了完整性,

  • 内核模式调度是一种非常低级的机制,只能从内核访问。这意味着你只能使用它,如果你开发操作系统内部或某种驱动程序。这种调度很难使用,任何错误,整个操作系统都被冻结/卡住/崩溃/蓝屏死机或过渡到任何其他不可预测的状态。内核级线程需要大量的开发和测试工作。例如,Windows 任务调度程序或内存管理服务使用内核模式调度。你能想象一下编写这些组件需要多少时间才能使它们在高度多线程的环境中保持稳定吗?
于 2013-02-26T16:04:10.923 回答