“协程”和“线程”之间的区别?
“协程”和“线程”之间有什么区别?
协程是顺序处理的一种forms:在任何给定的时间只有一个正在执行(就像子程序AKA程序AKA的function – 它们只是在彼此之间stream畅地传递接力棒)。
线程(至less在概念上)是并发处理的一种forms:多个线程可以在任何给定的时间执行。 (传统上,在单CPU,单核机器上,并发性是在OS的帮助下模拟的 – 现在,由于如此多的机器是多CPU和/或多核的,线程将事实上同时执行,不只是“概念上”)。
首先阅读: 并发VS并行 – 有什么区别?
并发性是提供交错执行的任务分离。 并行是为了提高速度而同时执行多个工作。 – https://github.com/servo/servo/wiki/Design
简短的回答:使用线程,操作系统根据其调度程序抢先切换正在运行的线程,这是操作系统内核中的一种algorithm。 使用协程,程序员和编程语言决定何时切换协程; 换句话说,通过在设定点暂停和恢复function,典型地(但不一定)在单个线程内,任务协作地多任务。
长时间的回答:与线程相比,操作系统先发制人,线程交换是协作的,这意味着程序员(也可能是编程语言及其运行时)控制交换机何时发生。
与先发制人的线程相比,协同开关是协作的(程序员在开关切换时控制)。 内核不参与协程开关。 – http://www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html
支持本地线程的语言可以将其线程(用户线程)执行到操作系统的线程( 内核线程 )上。 每个进程至less有一个内核线程。 内核线程就像进程一样,除了它们在拥有进程的过程中与其他所有线程共享内存空间。 一个进程“拥有”其所有分配的资源,如内存,文件句柄,套接字,设备句柄等,这些资源在其内核线程之间共享。
操作系统调度程序是运行每个线程一段时间(在单个处理器计算机上)的内核的一部分。 调度程序为每个线程分配时间(timeslicing),如果线程在这段时间内还没有完成,调度程序会抢占它(中断它并切换到另一个线程)。 multithreading可以在多处理器机器上并行运行,因为每个线程都可以(但不一定必须)调度到单独的处理器上。
在单个处理器的机器上,线程被快速定时和抢占(在Linux之间切换)(默认时间片是100ms),这使得它们并发。 但是,它们不能并行(同时)运行,因为单核处理器一次只能运行一件事情。
协程和/或生成器可用于实现协同function。 它们不是在内核线程上运行,而是由操作系统调度,它们运行在一个单独的线程中,直到它们产生或者完成,产生由程序员确定的其他function。 带有生成器的语言,比如Python和ECMAScript 6,可以用来构build协程。 asynchronous/等待(在C#中看到,Python,ECMAscript 7,Rust)是在产生期货/承诺的生成器函数之上构build的抽象。
在某些情况下, 协程可能会引用堆栈函数,而发生器可能会引用堆栈函数。
纤维 , 轻量级线程和绿色线程是协程或协程types的其他名称。 他们有时可能会更像编程语言中的操作系统线程(通常是故意的),但它们并不像真正的线程那样并行运行,而是像协同程序一样工作。 (根据语言或实现,这些概念之间可能有更具体的技术特点或不同之处。)
例如,Java有“ 绿色线程 ”; 这些是由Java虚拟机(JVM)调度的线程,而不是本地在底层操作系统的内核线程上。 这些不是并行运行,或者利用多个处理器/内核 – 因为这需要本地线程! 由于它们不是由操作系统调度的,它们更像是协程而不是内核线程。 绿色线程是Java直到本地线程被引入到Java 1.2之前所使用的线程。
线程消耗资源。 在JVM中,每个线程都有自己的堆栈,通常为1MB。 64k是JVM中每个线程允许的最小堆栈空间量。 线程堆栈大小可以在JVM的命令行上进行configuration。 尽pipe有这个名字,线程并不是免费的,因为它们的使用资源(如每个线程需要自己的堆栈),线程本地存储(如果有的话)以及线程调度/上下文切换/ CPU高速caching失效的代价。 这是协程在性能关键,高度并发应用程序中受欢迎的原因之一。
Mac OS将只允许一个进程分配大约2000个线程,而Linux为每个线程分配8MB的堆栈,并且只允许多less个线程适合物理RAM。
因此,线程是最重的(在内存使用和上下文切换时间方面),然后是协程,最后是发生器是最轻的重量。
大约晚了7年,但是这里的答案缺less了关于协同例程和线程的一些上下文。 为什么协程在最近受到如此多的关注,我将在什么时候使用它们与线程相比?
首先,如果协同运行(从不并行 ),为什么会有人比线程更喜欢它们?
答案是,协程可以提供非常高的并发性水平, 而且开销很小 。 通常在一个线程环境中,最多有30-50个线程,然后实际上调度这些线程(通过系统调度程序) 大大减less了线程真正有用的工作量。
那么线程你可以有并行性,但不是太多的并行性,是不是比在单个线程中运行的协同例程更好? 不一定。 记住,一个协同例程仍然可以在没有调度程序开销的情况下执行并发,它只是简单地pipe理上下文切换。
例如,如果你有一个例程做一些工作,它执行一个你知道会阻塞一段时间的操作(例如一个networking请求),通过一个协同例程,你可以立即切换到另一个例程,而不需要包括系统调度器这个决定 – 是的程序员必须指定何时可以切换。
有很多例程在做很less的工作,并且彼此之间自动切换,所以你已经达到了一个调度程序无法实现的效率水平。 现在可以有数千个协同工作,而不是数十个线程。
因为您的例程现在可以相互切换预定点,所以现在也可以避免locking共享数据结构(因为您永远不会告诉您的代码在关键部分切换到另一个协程)
另一个好处是内存使用率要低得多。 使用线程模型时,每个线程都需要分配自己的堆栈,因此您的内存使用量会随着线程数的增加而线性增长。 使用协同例程,您拥有的例程数与您的内存使用量没有直接关系。
最后,因为在一些编程语言(比如Python)中,你的线程不能并行运行 ,所以co-routines正在受到很多关注 – 它们和coroutine一样同时运行,但没有低内存和免费的调度开销。
总之:抢先。 协程就像是一个杂技演员,他们相互交接着一个排练良好的点。 线程(真正的线程)几乎可以在任何时候中断,然后再恢复。 当然,这带来了各种各样的资源冲突问题,因此Python的臭名昭着的GIL – 全球解释器锁。
许multithreading实现实际上更像是协程。
这取决于你使用的语言。 例如在Lua中它们是相同的东西 (协程的variablestypes称为thread
)。
通常虽然协程实现自愿屈服,程序员决定在哪里yield
,即控制另一个例程。
而线程则由OS自动pipe理(停止和启动),甚至可以在多核CPU上同时运行。