12

这是我的理解;请更正/添加:

在纯 ULT 中,多线程进程本身执行线程调度。因此,内核本质上没有注意到差异,并认为它是一个单线程进程。如果一个线程进行阻塞系统调用,则整个进程都会被阻塞。即使在多核处理器上,一次也只能运行一个进程线程,除非进程被阻塞。不过,我不确定 ULT 有多大帮助。

在纯 KLT 中,即使一个线程被阻塞,内核也会调度同一进程的另一个(就绪)线程。(在纯 KLT 的情况下,我假设内核创建了进程的所有线程。)

此外,使用 ULT 和 KLT 的组合,如何将 ULT 映射到 KLT?

4

2 回答 2

22

你的分析是正确的。操作系统内核不知道用户级线程。从它的角度来看,进程是一个不透明的黑匣子,偶尔会进行系统调用。因此,如果该程序有 100,000 个用户级线程但只有一个内核线程,那么该进程一次只能运行一个用户级线程,因为只有一个内核级线程与之关联。另一方面,如果一个进程有多个内核级线程,那么如果有一个多核机器,它就可以并行执行多个命令。

一个常见的折衷方案是让程序请求一些固定数量的内核级线程,然后让其自己的线程调度程序将用户级线程酌情分配到这些内核级线程上。这样,多个 ULT 可以并行执行,并且程序可以对线程的执行方式进行细粒度控制。

至于这个映射是如何工作的——有很多不同的方案。您可以想象用户程序使用多种不同调度系统中的任何一种。事实上,如果你做这个替换:

内核线程 <---> 处理器内核

用户线程 <---> 内核线程

那么操作系统可以用来将内核线程映射到内核的任何方案也可以用来将用户级线程映射到内核级线程。

希望这可以帮助!

于 2013-02-09T21:52:42.543 回答
3

首先,templatetypedef的答案很漂亮;我只是想稍微扩展他的回答。

我觉得有一个领域需要扩展一点:ULT 和 KLT 的组合。要了解重要性(维基百科标记混合线程),请考虑以下示例:

考虑一个多线程程序(多个 KLT),其中 KLT 多于可用逻辑内核。为了有效地使用每个内核,正如您所提到的,您希望调度程序将阻塞的 KLT 切换到处于就绪状态且未阻塞的 KLT。这可确保核心减少其空闲时间。不幸的是,切换 KLT 对调度程序来说代价高昂,并且会消耗相对大量的 CPU 时间。

这是混合线程可以提供帮助的一个领域。考虑一个具有多个 KLT 和 ULT 的多线程程序。正如templatetypedef所指出的,每个 KLT 一次只能运行一个 ULT。如果 ULT 被阻塞,我们仍然希望将其切换为没有阻塞的 ULT。幸运的是,ULT 比 KLT 轻得多,因为分配给 ULT 的资源更少,并且它们不需要与内核调度程序交互。从本质上讲,切换 ULT 几乎总是比切换 KLT 更快。因此,相对于第一个示例,我们能够显着减少核心空闲时间。

现在,当然,所有这些都取决于用于实现 ULT 的线程库。有两种方法(我可以想出)将 ULT 映射到 KLT。

  1. 所有KLT 的ULT 集合

    这种情况在共享内存系统上是理想的。本质上,每个 KLT 都可以访问 ULT 的“池”。理想情况下,线程库调度程序将根据请求将 ULT 分配给每个 KLT,而不是 KLT 单独访问池。如果不使用锁或类似的东西实现,后者可能会导致竞争条件或死锁。

  2. 每个KLT的 ULT 集合( Qthreads

    这种情况在分布式内存系统上是理想的。每个 KLT 都会有一组 ULT 来运行。缺点是用户(或线程库)必须在 KLT 之间划分 ULT。这可能会导致负载不平衡,因为不能保证所有 ULT 将完成相同数量的工作,并且完成大致相同的时间。解决方案是允许 ULT 迁移;也就是说,在 KLT 之间迁移 ULT。

于 2013-07-26T21:36:58.297 回答