“coroutine”和“thread”之间有什么区别?
答案 0 :(得分:133)
首先阅读: Concurrency vs Parallelism - What is the difference?
并发是分离任务以提供交错 执行。并行性是多个的同时执行 工作以增加速度。 - https://github.com/servo/servo/wiki/Design
简答:对于线程,操作系统根据其调度程序(操作系统内核中的算法)预先切换运行线程。使用协同程序,程序员和编程语言决定何时切换协同程序;换句话说,任务通过在设定点暂停和恢复功能来协同多任务,通常(但不一定)在单个线程内。
答案很长:与操作系统预先安排的线程相比,协同开关是协作的,这意味着程序员(可能还有编程语言及其运行时)控制何时切换会发生。
与先发制人的线程形成鲜明对比的是协同开关 合作(程序员控制何时发生切换)。该 内核不参与协程开关。 - http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html
支持本机线程的语言可以将其线程(用户线程)执行到操作系统的线程(内核线程)。每个进程至少有一个内核线程。内核线程就像进程一样,除了它们在拥有进程中与该进程中的所有其他线程共享内存空间。一个过程"拥有"所有已分配的资源,如内存,文件句柄,套接字,设备句柄等,这些资源都在其内核线程之间共享。
操作系统调度程序是运行每个线程一段时间(在单个处理器计算机上)的内核的一部分。调度程序为每个线程分配时间(时间分隔),如果线程在该时间内没有完成,则调度程序会抢占它(中断它并切换到另一个线程)。多个线程可以在多处理器计算机上并行运行,因为每个线程可以(但不一定必须)安排到单独的处理器上。
在单处理器计算机上,线程被快速切换并被抢占(在其间切换)(在Linux上,默认时间片为100毫秒),这使它们并发。但是,它们不能并行(同时)运行,因为单核处理器一次只能运行一件事。
协同程序和/或生成器可用于实现协作功能。它们不是在内核线程上运行并由操作系统调度,而是在单个线程中运行,直到它们产生或完成,从而产生由程序员确定的其他函数。使用生成器的语言(如Python和ECMAScript 6)可用于构建协同程序。 Async / await(见于C#,Python,ECMAscript 7,Rust)是一个基于生成函数的抽象,它产生期货/承诺。
在某些情况下, coroutines 可能指的是堆叠函数,而生成器可能指的是无堆栈函数。
光纤,轻量级线程和绿色线程是协同程序或类似协程的其他名称。它们有时看起来(通常是故意的)更像是编程语言中的操作系统线程,但它们并不像真正的线程那样并行运行,而是像协程一样工作。 (这些概念可能有更具体的技术特点或差异,具体取决于语言或实施。)
例如,Java有" 绿色线程&#34 ;;这些是由Java虚拟机(JVM)调度的线程,而不是本机在底层操作系统的内核线程上调度的线程。这些并不是并行运行或利用多个处理器/内核 - 因为这需要本机线程!由于它们不是由操作系统安排的,因此它们更像是协程而不是内核线程。绿色线程是Java在将原生线程引入Java 1.2之前使用的。
线程消耗资源。在JVM中,每个线程都有自己的堆栈,通常为1MB。 64k是JVM中每个线程允许的最小堆栈空间量。可以在JVM的命令行上配置线程堆栈大小。尽管名称,线程不是免费的,因为它们的使用资源,如每个线程需要自己的堆栈,线程本地存储(如果有的话),以及线程调度/上下文切换/ CPU缓存失效的成本。这是协同程序在性能关键,高度并发应用程序中变得流行的部分原因。
Mac OS只允许进程分配大约2000个线程,Linux每个线程分配8MB堆栈,并且只允许适合物理RAM的线程数。
因此,线程是最重的(在内存使用和上下文切换时间方面),然后是协同程序,最后生成器是最轻的权重。
答案 1 :(得分:89)
线程(至少在概念上)是一种并发处理形式:多个线程可能在任何给定时间执行。 (传统上,在单CPU,单核机器上,通过操作系统的一些帮助模拟并发性 - 如今,由于许多机器是多CPU和/或多核,线程将事实上< / em>同时执行,而不仅仅是“概念上”。
答案 2 :(得分:80)
大约7年后,但这里的答案缺少一些关于协同例程与线程的背景。为什么 coroutines 最近受到如此多的关注,与线程相比,何时使用它们?
首先,如果协同程序同时运行 (从不在并行),为什么有人会比线程更喜欢它们?
答案是协同程序可以提供非常高的并发性,其中开销非常小。通常在线程环境中,在浪费开销之前,最多有30-50个线程实际调度这些线程(由系统调度程序)显着会削减线程实际执行有用工作的时间。
好吧,对于线程,你可以有并行性,但不是太多的并行性,是不是仍然比在单个线程中运行的协同例程更好?不一定。记住,一个协同例程仍然可以在没有调度程序开销的情况下进行并发 - 它只是管理上下文切换本身。
例如,如果你有一个例程做一些工作,并且它执行一个操作,你知道会阻塞一段时间(即一个网络请求),通过一个协同例程,你可以立即切换到另一个例程,而无需包括此决定中的系统调度程序 - 是的,程序员必须指定协同例程何时可以切换。
由于许多例程正在进行非常少量的工作并且彼此之间自愿切换,因此您已达到了调度程序无法实现的效率水平。现在,您可以使用数千个协同程序,而不是数十个线程。
因为您的例程现在可以在预先确定的点之间切换,所以您现在也可以避免锁定共享数据结构(因为您永远不会告诉您的代码切换到另一个协同中间的协程关键部分)
另一个好处是内存使用率大大降低。使用线程模型,每个线程都需要分配自己的堆栈,因此您的内存使用量会随着线程数的增加而线性增长。通过协同例程,您拥有的例程数量与您的内存使用量没有直接关系。
最后,协同例程受到了很多关注,因为在某些编程语言(例如Python)中,线程无论如何都不能并行运行 - 它们像协同程序一样同时运行,但没有低内存和免费调度开销。
答案 3 :(得分:16)
总之:先发制人。协同程序就像玩杂耍者一样,不断向对方传递一个经过良好排练的点。线程(真正的线程)几乎可以在任何点被中断,然后稍后恢复。当然,这带来了各种资源冲突问题,因此Python臭名昭着的GIL - Global Interpreter Lock。
许多线程实现实际上更像是协同程序。
答案 4 :(得分:8)
这取决于您使用的语言。例如,在Lua they are the same thing中(协程的变量类型称为thread
)。
通常虽然协同程序实现了自愿屈服,但程序员决定在哪里yield
,即控制另一个例程。
操作系统自动管理(停止和启动)线程,它们甚至可以在多核CPU上同时运行。
答案 5 :(得分:4)
讨论晚了12年,但协程名称中有解释。协程可以分解为Co和Routine。
在这种情况下,例程只是一系列操作/动作,通过执行/处理例程,操作序列将按照指定的完全相同的顺序逐个执行。
Co代表合作。一个协例程被要求(或更好地期望)中止执行,以使其他协例程也有执行的机会。因此,一个协程是关于(自愿地)共享CPU资源,以便其他人可以使用与自己正在使用的资源相同的资源。
另一方面,线程不需要暂停其执行。挂起对线程是完全透明的,并且底层硬件强制线程将其自身挂起。这样做的方式也使得它对线程几乎是透明的,因为它不会得到通知,并且它的状态不会更改,而是保存并在允许线程继续执行时恢复。
一个不正确的事实是,不能并发执行协同例程,并且不会发生竞争条件。它取决于运行协同例程的系统,并且很容易对协同例程进行成像。
协同例程如何暂停自身无关紧要。在Windows 3.1 int 03中,它被编织到任何程序中(或必须放在其中),在C#中,我们增加了yield。