单核上的多线程有什么意义?
What's the point of multi-threading on a single core?
我最近一直在玩 Linux 内核,并重新回到大学 OS 课程的时代。
就像当年一样,在玩线程之类的。一直以来,我一直假设线程 自动 运行 在多个内核上并发 但我最近发现您实际上必须显式编码才能处理多个内核。
那么单核多线程有什么意义呢?我能想到的唯一例子是大学写 client/server 程序时的例子,但这似乎是一个弱点。
嗯,当你在单核上说多线程时,你需要考虑一些事情。例如,您正在使用的线程 API - 是用户级别还是内核级别。很可能来自你的问题,我相信你正在使用用户级线程。
现在,用户级线程,取决于主机 OS 或 API 本身,可以映射到单个内核线程或多个。许多关系都是可能的,例如 1-1、多-1 或多-多。
现在,如果只有一个核心,您的 OS 仍然可以为您提供多个内核级线程,这些线程可能表现为 CPU 的多个进程。在这种情况下,OS 将在内核线程上为您提供时间切片(和多编程),从而导致超快的上下文切换,并通过用户级别 API - you/your 代码看起来具有多线程功能。
另请注意,尽管您的处理器是单核,但根据制造商的不同,它可以是超线程的,并且具有超深管道,允许并发 运行 内核线程,开销非常低。
供参考:检查 Intel/AMD 体系结构以及各种 OS 提供内核线程的方式。
首先,程序不仅要计算,还要等待 input/output,因此可以认为是在 I/O 处理器上执行。所以即使单核机器也是多处理器机器,使用多线程是有道理的。
其次,为了模块化,一个任务可以拆分成多个线程。
All this time I had been assuming that threads were automatically
running concurrently on multiple cores but I've recently discovered
that you actually have to explicitly code for handling multiple cores.
以上内容对于任何广泛使用的现代 OS 都是不正确的。例如,Linux 的所有调度程序都会在不同的内核上自动调度线程,甚至在必要时自动将线程从一个内核移动到另一个内核以最大限度地利用内核。有一些 API 允许您修改调度程序的行为,但这些 API 通常用于禁用自动线程到核心调度,而不是启用它。
So what's the point of multi-threading on a single core?
假设您有一个 GUI 程序,其目的是执行昂贵的计算(例如,渲染 3D 图像或 Mandelbrot 集)然后显示结果。假设这个计算需要 30 秒才能完成这个特定的 CPU。如果您以明显的方式实现该程序,并且只使用一个线程,那么在执行计算时,用户的 GUI 控件将在 30 秒内没有响应——用户将无法对您的程序执行任何操作,并且可能无法用他的电脑做任何事。由于用户希望 GUI 控件始终响应,这将是糟糕的用户体验。
如果您使用两个线程(一个 GUI 线程和一个渲染线程)实现该程序,另一方面,用户将能够单击按钮、调整大小 window、退出程序、选择菜单项等,即使在计算正在执行时,因为 OS 能够唤醒 GUI 线程并允许它在必要时处理 mouse/keyboard 事件。
当然,可以用单线程编写此程序并保持其 GUI 响应,方法是编写单线程来执行仅几毫秒的计算,然后检查是否有可用的 GUI 事件处理、处理它们,然后返回进行更多计算等。但是,如果您以这种方式编写您的应用程序,那么您实际上是在您的应用程序中编写您自己的(非常原始的)线程调度程序,那么为什么要重新发明轮子呢?
第一个版本的 MacOS 被设计为 运行 在单核上,但没有真正的多线程概念。这迫使每个应用程序开发人员正确地实现一些手动线程管理——即使他们的应用程序没有任何扩展计算,他们也必须使用 CPU 明确指示何时完成,例如通过调用 WaitNextEvent。多线程的缺乏使得早期(MacOS-X 之前)版本的 MacOS 在多任务处理方面出了名的不可靠,因为一个写得不好的应用程序可能会使整个计算机停止运转。
多线程不仅仅是为了利用多核。
您需要多个进程才能进行多任务处理。出于类似的原因,您可以拥有多个线程,与进程相比,它们是轻量级的。
您可能不想一直为诸如阻塞之类的事情生成进程 I/O。这可能有点矫枉过正。
还有纤维,更轻。所以我们有进程、线程和纤程来满足不同层次的需求。
我最近一直在玩 Linux 内核,并重新回到大学 OS 课程的时代。
就像当年一样,在玩线程之类的。一直以来,我一直假设线程 自动 运行 在多个内核上并发 但我最近发现您实际上必须显式编码才能处理多个内核。
那么单核多线程有什么意义呢?我能想到的唯一例子是大学写 client/server 程序时的例子,但这似乎是一个弱点。
嗯,当你在单核上说多线程时,你需要考虑一些事情。例如,您正在使用的线程 API - 是用户级别还是内核级别。很可能来自你的问题,我相信你正在使用用户级线程。 现在,用户级线程,取决于主机 OS 或 API 本身,可以映射到单个内核线程或多个。许多关系都是可能的,例如 1-1、多-1 或多-多。 现在,如果只有一个核心,您的 OS 仍然可以为您提供多个内核级线程,这些线程可能表现为 CPU 的多个进程。在这种情况下,OS 将在内核线程上为您提供时间切片(和多编程),从而导致超快的上下文切换,并通过用户级别 API - you/your 代码看起来具有多线程功能。 另请注意,尽管您的处理器是单核,但根据制造商的不同,它可以是超线程的,并且具有超深管道,允许并发 运行 内核线程,开销非常低。 供参考:检查 Intel/AMD 体系结构以及各种 OS 提供内核线程的方式。
首先,程序不仅要计算,还要等待 input/output,因此可以认为是在 I/O 处理器上执行。所以即使单核机器也是多处理器机器,使用多线程是有道理的。
其次,为了模块化,一个任务可以拆分成多个线程。
All this time I had been assuming that threads were automatically running concurrently on multiple cores but I've recently discovered that you actually have to explicitly code for handling multiple cores.
以上内容对于任何广泛使用的现代 OS 都是不正确的。例如,Linux 的所有调度程序都会在不同的内核上自动调度线程,甚至在必要时自动将线程从一个内核移动到另一个内核以最大限度地利用内核。有一些 API 允许您修改调度程序的行为,但这些 API 通常用于禁用自动线程到核心调度,而不是启用它。
So what's the point of multi-threading on a single core?
假设您有一个 GUI 程序,其目的是执行昂贵的计算(例如,渲染 3D 图像或 Mandelbrot 集)然后显示结果。假设这个计算需要 30 秒才能完成这个特定的 CPU。如果您以明显的方式实现该程序,并且只使用一个线程,那么在执行计算时,用户的 GUI 控件将在 30 秒内没有响应——用户将无法对您的程序执行任何操作,并且可能无法用他的电脑做任何事。由于用户希望 GUI 控件始终响应,这将是糟糕的用户体验。
如果您使用两个线程(一个 GUI 线程和一个渲染线程)实现该程序,另一方面,用户将能够单击按钮、调整大小 window、退出程序、选择菜单项等,即使在计算正在执行时,因为 OS 能够唤醒 GUI 线程并允许它在必要时处理 mouse/keyboard 事件。
当然,可以用单线程编写此程序并保持其 GUI 响应,方法是编写单线程来执行仅几毫秒的计算,然后检查是否有可用的 GUI 事件处理、处理它们,然后返回进行更多计算等。但是,如果您以这种方式编写您的应用程序,那么您实际上是在您的应用程序中编写您自己的(非常原始的)线程调度程序,那么为什么要重新发明轮子呢?
第一个版本的 MacOS 被设计为 运行 在单核上,但没有真正的多线程概念。这迫使每个应用程序开发人员正确地实现一些手动线程管理——即使他们的应用程序没有任何扩展计算,他们也必须使用 CPU 明确指示何时完成,例如通过调用 WaitNextEvent。多线程的缺乏使得早期(MacOS-X 之前)版本的 MacOS 在多任务处理方面出了名的不可靠,因为一个写得不好的应用程序可能会使整个计算机停止运转。
多线程不仅仅是为了利用多核。
您需要多个进程才能进行多任务处理。出于类似的原因,您可以拥有多个线程,与进程相比,它们是轻量级的。
您可能不想一直为诸如阻塞之类的事情生成进程 I/O。这可能有点矫枉过正。
还有纤维,更轻。所以我们有进程、线程和纤程来满足不同层次的需求。