这是我的理解;请更正/添加:
在纯ULT中,多线程进程本身执行线程调度。因此,内核基本上没有注意到差异并将其视为单线程进程。如果一个线程进行阻塞系统调用,则会阻止整个进程。 即使在多核处理器上,也只会一次运行该进程的一个线程,除非该进程被阻止。我不确定ULT如何提供帮助。
在纯KLT中,即使线程被阻塞,内核也会调度同一进程的另一个(就绪)线程。 (在纯KLT的情况下,我假设内核创建了进程的所有线程。)
此外,使用ULT和KLT的组合,ULT如何映射到KLT?
答案 0 :(得分:17)
您的分析是正确的。操作系统内核不了解用户级线程。从它的角度来看,进程是一个不透明的黑盒子,偶尔会进行系统调用。因此,如果该程序具有100,000个用户级线程但只有一个内核线程,则该进程一次只能运行一个用户级线程,因为只有一个内核级线程与之关联。另一方面,如果进程有多个内核级线程,那么如果有多核机器,它可以并行执行多个命令。
这些之间的一个常见折衷方案是让程序请求一些固定数量的内核级线程,然后让自己的线程调度程序将用户级线程分配到这些内核级线程上。这样,多个ULT可以并行执行,程序可以对线程的执行方式进行细粒度控制。
至于这种映射是如何工作的 - 有很多不同的方案。您可以想象用户程序使用多个不同调度系统中的任何一个。事实上,如果你做这个替换:
内核线程< --->处理器核心
用户线程< --->内核线程
然后,操作系统可用于将内核线程映射到核心的任何方案也可用于将用户级线程映射到内核级线程。
希望这有帮助!
答案 1 :(得分:2)
除此之外,templatetypedef的答案很美;我只是想稍微扩展他的回应。
我觉得有一个领域需要扩展一点: ULT和KLT的组合。要了解重要性(维基百科标记为hybrid threading),请考虑以下示例:
考虑一个多线程程序(多个KLT),其中KLT比可用的逻辑核心多。为了有效地使用每个核心,正如您所提到的,您希望调度程序切换出阻塞的KLT,以及处于就绪状态且不阻塞的KLT。这可确保核心减少其空闲时间。不幸的是,切换KLT对于调度程序而言是昂贵的,并且它消耗相当大的CPU时间。
这是混合线程可能有用的一个领域。考虑一个具有多个KLT和ULT的多线程程序。正如 templatetypedef 所指出的那样,每个KLT一次只能运行一个ULT。如果ULT阻塞,我们仍然希望将其切换为非阻塞的。幸运的是,ULT比KLT更轻量级,因为分配给ULT的资源更少,并且它们不需要与内核调度程序交互。从本质上讲,切换ULT几乎总是比切换KLT更快。因此,相对于第一个示例,我们能够显着减少核心空闲时间。
现在,当然,所有这些都取决于用于实现ULT的线程库。有两种方法(我可以提出)将ULT“映射”到KLT。
一系列ULT 适用于所有 KLT的
这种情况在共享内存系统上非常理想。每个KLT都可以访问ULT的“池”。理想情况下,线程库调度程序会根据请求将ULT分配给每个KLT,而不是KLT单独访问池。如果没有用锁或类似的东西实现,后者可能会导致竞争条件或死锁。
每个 KLT(Qthreads)
的ULT 集合这种情况在分布式内存系统上非常理想。每个KLT都有一系列ULT可供运行。缺点是用户(或线程库)必须在KLT之间划分ULT。这可能会导致负载不平衡,因为无法保证所有ULT都能完成相同的工作量并完成大致相同的时间。解决方案是允许ULT迁移;也就是说,在KLT之间迁移ULT。