2

有几种线程模型可用于在应用程序中调度线程:

  • 1:1(内核级线程):用户创建的每个线程都映射到内核中的一个调度线程。
  • N:1(用户级线程):用户在单个应用程序中创建的所有线程实际上都映射到单个调度的内核线程。
  • M:N(混合线程):用户在应用程序中创建的 M 个线程映射到 N 个内核线程。

用户级线程被认为比内核级线程更快,因为内核级的上下文切换比用户级更昂贵。用户级线程的一大缺点是它们不使用多处理器系统,因为它们只使用一个内核级线程。

有一些文章告诉 M:N 线程模型最好使用 N 作为 CPU 内核的数量(这里是一个例子)。通过这种方式,我们可以实现 1:1 和 N:1 线程模型的优势。

我的问题是:

  1. 当我们使用内核级线程时,我们还会在执行期间获得“额外”时间片(与用户级线程相反),所以它是否弥补了缓慢的上下文切换?
  2. 为什么 CPU 内核的数量在这里甚至是相关的?据我了解,这里的 CPU 内核数量是非常透明的,因为即使我们使用确切数量的内核线程,也无法保证它们确实是同时执行的,因为其他内核可以执行来自其他进程的其他线程,并且“我们的' 线程之后可能仍会使用上下文切换。因此,无论我们拥有多少 CPU 内核,他们都使用上下文切换。我在这里错了吗?
4

3 回答 3

1

用户级线程被认为比内核级线程快

根据工作负载、操作系统、硬件和绿色线程实现,两者都可以更快。

它不能弥补缓慢的上下文切换吗?

有时。通常没有。内核线程有一个堆栈,当您拥有数千个线程时,它们会消耗千兆字节的 RAM,并且当它们进行上下文切换时,您可以保证有大量的缓存未命中。那是假设您的工作负载 IO 繁重并且您的上下文切换很多。

为什么 CPU 内核的数量在这里甚至是相关的?

无关紧要。您应该使用硬件线程数,许多现代 CPU 每个内核有 2 个硬件线程。

其他内核可以执行来自其他进程的其他线程

如果它们花费大量时间,则意味着您有 2 个进程加载系统。在这种情况下,更好的方法可能是使用 50% 的硬件线程。当人们设计需要资源的软件时,他们通常认为这将是计算机的主要工作量。

由于上下文切换,创建一个新的内核级线程(在具有单个硬件线程的系统上)通常不会给我额外的 CPU 时间

如果还有其他进程也需要 100% CPU,使用 2 个线程,您确实会获得额外的 CPU 时间。但这是罕见的边缘情况,由于系统无响应,用户会点击重置按钮。通常,除非涉及阻塞 IO 或安全性,否则创建比硬件线程更多的内核线程几乎没有意义。

于 2019-12-01T23:53:33.203 回答
1

Go(lang)?,作为一个例子,利用这个模型来实现它的并发性。goroutine 可能会从与调度它的内核线程不同的内核线程中的系统调用返回。Go 旨在并声称既高效又实现高度利用。

一个问题是线(锤子)有许多应用(钉状物体)。 并发编程是一种与系统运行特性相匹配的程序组织形式,与旨在通过最大限度地利用资源来减少执行时间的并行编程有很大不同。有相当多的重叠,因为通常并发程序比它们的顺序等效程序更快且响应更快。

(1):额外的时间片。可能有一些调度程序确实如此,但您所描述的是一个过载的系统——它需要做的工作比可用的资源多。过载可能是暂时的,但要使其成为一种设计选择,您将受到调度程序升级的支配,该升级程序在进程/作业/会话/之间取得平衡?而不是线程;你的额外是一个实现细节。

(2): 是的,你在这里是对的多于错的。仅创建 N 个内核线程是不够的,除非您的机器具有某种形式的coscheduling,但仍然可能会受到需要在真实 io 上同步的系统调用的影响(例如read(2))。冒着成为 Go-fanboi 的风险,Go 调度程序通过在 #execution 单元之外保留停放内核线程的系统调用 slush 资金来规避这一点;所以真的有一个 L:M:N 线程模型。

于 2019-12-02T18:04:33.167 回答
1

Alan Cox 曾经说过,在多核架构普及之前,“计算机是一个状态机。线程是为那些不能编程状态机的人准备的。”

至少可能在不同内核上安排内核线程是有意义的。根据我的经验,在绝大多数情况下,用户线程只不过是一种不必要的昂贵抽象,旨在让您无需明确考虑管理作为 CPU 核心的状态机。

当然,这很好。我们并不总是,也许甚至通常不需要顶级性能。但是,如果您的方案不需要将最高性能作为首要考虑因素,我就不会担心线程模型,只需使用最简单的模型。如果您确实关心,我会使用 1:1 内核线程并处理单核多路复用明确地。

于 2019-12-02T06:20:46.300 回答