【问题标题】:Are .NET threads different from operating system threads?.NET 线程与操作系统线程不同吗?
【发布时间】:2023-03-06 10:52:01
【问题描述】:
  1. .NET 线程是轻量级用户模式线程还是内核模式操作系统线程?

  2. 另外,除了 SQL Server,.NET 线程和操作系统线程之间是否存在一一对应关系?

我也很感兴趣,因为 Thread 类有一对对称的方法,名为 BeginThreadAffinityEndThreadAffinity,其文档巧妙地表明 .NET 线程是对真实操作系统线程的轻量级抽象。

另外,我不久前在一些堆栈溢出线程本身上读到,微软试图在 CLR 中保持这种分离,就像 SQL Server 所做的那样。我记得有一些项目正在为此使用 Fiber API,但我不能说我理解了我所读内容的所有细节。

我想要一些关于这个主题的更详细的文献,例如 .NET 线程的内部结构与 Windows 创建的线程的内部结构。虽然有大量关于 Windows 创建的线程结构的信息,但 Jeffrey Richter 的 Advanced Windows Programming 书就是其中的来源之一,例如,我找不到任何专门介绍内部线程的文献.NET 线程的结构。

有人可能会争辩说,此信息可在 .NET 源代码中获得,该源代码现已公开,或者使用诸如 Reflector 或 IL Spy 之类的反汇编程序,但我没有看到任何代表线程控制块 (TCB ) 和程序计数器 (PC) 和堆栈指针 (SP) 或线程的等待队列,或线程当前属于 Thread 类的队列列表。

我在哪里可以阅读到这方面的信息?文档中是否提到了其中任何一个?我已经阅读了所有these pages from the MSDN,但他们似乎没有提及。

【问题讨论】:

  • 在普通的桌面应用程序和 Web 应用程序中,一个 .NET 线程对应一个操作系统线程,至少在 Windows 上是这样。在 SQL CLR 托管世界中,这不一定是真的,而且我不知道在其他平台上运行的 .NET 核心运行时。我不认为 .NET 线程被记录为对应于 os 内核线程,这很可能只是一个实现细节。
  • @LasseV.Karlsen 非常感谢。
  • @WaterCoolrv2 虽然安迪的回答是正确的,但它没有回答。问题的第一部分。在这里查看更详细的答案*.com/questions/49524899/…

标签: c# .net multithreading clr


【解决方案1】:

.NET 的线程确实是抽象的,但您基本上可以认为它们与操作系统线程几乎相同。尤其是在垃圾回收方面存在一些关键差异,但对于绝大多数程序员(阅读:不太可能启动 WinDBG 的程序员)而言,在功能上没有区别。

For more detail, read this

【讨论】:

    【解决方案2】:

    在决定如何进行并发之前,对预期性能和预期并发的性质有一些想法很重要。例如,.NET 是一个虚拟机,所以是模拟并发的。这意味着启动执行的开销比直接 OS 线程要多。

    如果您的应用程序打算按需进行大量并发,那么 .NET 任务或线程(甚至是 ThreadPool)的创建和开始执行速度会很慢。在这种情况下,您可能会受益于 Windows 操作系统线程。但是,您会想要使用非托管线程池(除非您像我一样,并且更喜欢编写自己的线程池)。

    如果按需启动不确定数量的线程的性能不是设计目标,那么您应该考虑 .NET 并发性。原因是它更容易编程,并且您可以利用影响编程语言内置的并发性的关键字。

    为了更好地理解这一点,我编写了一个测试应用程序,用于测试在不同并发执行单元中运行的算法。我在自己的非托管线程池、.NET 任务、.NET 线程和 .NET 线程池下运行了测试。

    将并发设置为 512,这意味着将尽快调用 512 个并发执行单元,我发现任何 .NET 的启动块都非常慢。我在几个系统上运行了测试,从具有 16gb RAM 的 i5 4 核桌面到 Windows Server 2012 R2,结果是相同的。

    我捕获了完成的并发执行单元的数量、每个单元启动所需的时间以及 CPU 核心利用率。每个测试的持续时间为 30 秒。

    所有测试的结果都是均衡的 CPU 核心利用率(与某些人的看法相反)。但是,任何 .NET 都会输掉比赛。在 30 秒内...

    .NET 任务和线程 (ThreadPool) 完成了 34 个任务,平均启动时间为 885 毫秒

    所有 512 个操作系统线程以 59 毫秒的平均启动时间运行完成。

    不管别人怎么说,从调用 API 开始执行单元和实际执行单元的路径在 .NET 中要长得多。

    【讨论】:

      最近更新 更多