线和纤维有什么区别?我听说过 ruby 中的纤维,并且我听说它们有其他语言版本,有人可以简单地向我解释一下线程和纤维之间的区别。
用最简单的术语来说,线程通常被认为是抢占式的(尽管这可能并不总是正确的,具体取决于操作系统),而光纤被认为是轻量级的协作线程。两者都是您的应用程序的单独执行路径。
使用线程:当前执行路径可能随时被中断或抢占(注意:此语句是一个概括,可能并不总是适用,具体取决于操作系统/线程包/等)。这意味着对于线程而言,数据完整性是一个大问题,因为一个线程可能会在更新一大块数据的过程中停止,从而使数据的完整性处于不良或不完整的状态。这也意味着操作系统可以通过同时运行多个线程来利用多个 CPU 和 CPU 内核,并将其留给开发人员来保护数据访问。
使用纤程:当前执行路径仅在纤程产生执行时才会中断(与上述相同)。这意味着光纤总是在明确定义的位置开始和停止,因此数据完整性不再是问题。此外,由于光纤通常在用户空间中进行管理,因此无需进行昂贵的上下文切换和 CPU 状态更改,这使得从一根光纤更改为下一个光纤非常有效。另一方面,由于没有两条纤程可以同时运行,因此仅使用纤程并不能利用多个 CPU 或多个 CPU 内核。
线程使用抢先调度,而纤程使用协作调度。
使用一个线程,控制流随时可能被中断,另一个线程可以接管。使用多个处理器,您可以同时运行多个线程(同时多线程,或 SMT)。因此,您必须非常小心并发数据访问,并使用互斥锁、信号量、条件变量等来保护您的数据。做对通常非常棘手。
使用纤程时,控制只会在您告诉它时切换,通常使用名为 yield()
的函数调用。这使得并发数据访问更容易,因为您不必担心数据结构或互斥体的原子性。只要您不屈服,就不会有被抢占的危险,并且不会有另一个纤程试图读取或修改您正在使用的数据。但是,结果是,如果您的光纤进入无限循环,则没有其他光纤可以运行,因为您没有屈服。
您还可以混合使用线和纤维,这会产生两者都面临的问题。不建议这样做,但如果小心操作,有时可能是正确的做法。
在 Win32 中,纤程是一种用户管理的线程。光纤有自己的堆栈和自己的指令指针等,但光纤不是由操作系统调度的:您必须显式调用 SwitchToFiber。相反,线程是由操作系统抢先调度的。所以粗略地说,纤程是在应用程序/运行时级别管理的线程,而不是真正的操作系统线程。
结果是光纤更便宜,应用程序对调度有更多的控制。如果应用程序创建大量并发任务和/或希望在它们运行时进行密切优化,这可能很重要。例如,数据库服务器可能选择使用光纤而不是线程。
(同一术语可能还有其他用法;如前所述,这是 Win32 定义。)
首先,我建议将 the difference between processes and threads 的解释作为背景材料阅读。
一旦你读过它,它就很简单了。线程既可以在内核中实现,也可以在用户空间中实现,或者两者可以混合使用。光纤基本上是在用户空间中实现的线程。
通常称为线程的是在内核中实现的执行线程:所谓的内核线程。内核线程的调度由内核独占处理,尽管内核线程可以根据需要通过休眠来自愿释放 CPU。内核线程的优点是它可以使用阻塞 I/O 并让内核担心调度。它的主要缺点是线程切换相对较慢,因为它需要陷入内核。
纤程是用户空间线程,其调度由单个进程下的一个或多个内核线程在用户空间处理。这使得光纤切换非常快。如果您将所有访问特定共享数据集的光纤分组在单个内核线程的上下文中,并由单个内核线程处理它们的调度,那么您可以消除同步问题,因为光纤将有效地串行运行并且您已经完成控制他们的日程安排。将相关纤程分组在单个内核线程下很重要,因为它们正在运行的内核线程可以被内核抢占。这一点在许多其他答案中都没有明确说明。此外,如果您在纤程中使用阻塞 I/O,则整个内核线程都是块的一部分,包括属于该内核线程的所有纤程。
在现代操作系统的第 11.4 节“Windows Vista 中的进程和线程”中,Tanenbaum 评论道:
虽然光纤是协作调度的,但如果有多个线程调度光纤,则需要进行大量仔细的同步以确保光纤不会相互干扰。为了简化线程和纤程之间的交互,通常只创建与运行它们的处理器一样多的线程,并将线程关联到每个只在一组不同的可用处理器上运行,甚至只在一个处理器上运行。然后每个线程可以运行特定的光纤子集,从而在线程和光纤之间建立一对多的关系,从而简化同步。即便如此,纤维仍然存在许多困难。大多数 Win32 库完全不知道纤程,并且尝试像线程一样使用纤程的应用程序会遇到各种故障。内核对纤程一无所知,当纤程进入内核时,它正在执行的线程可能会阻塞,内核会在处理器上调度任意线程,使其无法运行其他纤程。由于这些原因,除非从明确需要 Fiber 提供的功能的其他系统移植代码,否则很少使用 Fiber。
请注意,除了 Threads 和 Fibers,Windows 7 还引入了 User-Mode Scheduling:
用户模式调度 (UMS) 是一种轻量级机制,应用程序可以使用它来调度自己的线程。如果 UMS 线程在内核中阻塞,应用程序可以在不涉及系统调度程序的情况下在用户模式下在 UMS 线程之间切换,并重新获得对处理器的控制权。 UMS 线程与纤程的不同之处在于每个 UMS 线程都有自己的线程上下文,而不是共享单个线程的线程上下文。在用户模式下切换线程的能力使得 UMS 在管理大量需要很少系统调用的短期工作项方面比线程池更有效。
观看 Dave Probert: Inside Windows 7 - User Mode Scheduler (UMS) 可以获得有关线程、光纤和 UMS 的更多信息。
线程最初是作为轻量级进程创建的。以类似的方式,纤程是轻量级线程,通过屈服控制(简单地)依赖纤程本身来相互调度。
我想下一步将是每次您希望他们执行指令时都必须向他们发送信号的绞线(与我 5 岁的儿子不同:-)。在过去(甚至现在在某些嵌入式平台上),所有线程都是纤程的,没有抢占式,您必须编写线程才能表现得很好。
线程由操作系统调度(抢占式)。操作系统可以随时停止或恢复线程,但纤程或多或少地管理自己(合作)并相互让步。也就是说,程序员控制光纤何时进行处理以及该处理何时切换到另一根光纤。
线程通常依靠内核来中断线程,以便它或另一个线程可以运行(更好地称为抢占式多任务),而纤程使用协作式多任务处理,其中纤程本身放弃其运行时间,以便其他光纤可以运行。
一些有用的链接解释它比我可能做的更好:
http://en.wikipedia.org/wiki/Fiber_(computer_science)
http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing
http://en.wikipedia.org/wiki/Pre-emptive_multitasking
Win32 光纤定义实际上是 Sun Microsystems 建立的“Green Thread”定义。没有必要在某种线程上浪费术语纤维,即在用户代码/线程库控制下在用户空间执行的线程。
为了澄清这个论点,请查看以下评论:
使用超线程,多核 CPU 可以接受多个线程并将它们分配到每个内核上。
超标量流水线 CPU 接受一个线程执行,并使用指令级并行 (ILP) 更快地运行线程。我们可以假设一个线程被分解为在并行管道中运行的并行纤程。
SMT CPU 可以接受多个线程并将它们分解成指令纤程,以便在多个管道上并行执行,从而更有效地使用管道。
我们应该假设进程是由线程组成的,线程应该由纤维组成。考虑到这种逻辑,将纤维用于其他类型的线程是错误的。