“协程”和“线程”有什么区别?
当前回答
大约晚了7年,但是这里的答案缺少一些关于协同例程与线程的上下文。为什么协程最近受到如此多的关注,与线程相比,我什么时候会使用它们?
首先,如果协程是并发运行的(而不是并行运行的),为什么人们更喜欢协程而不是线程呢?
答案是协程可以用很少的开销提供非常高的并发性。通常在线程环境中,在实际调度这些线程所浪费的开销(由系统调度器)大大减少了线程实际执行有用工作的时间之前,您最多有30-50个线程。
好的,线程可以并行,但不能太多并行,这不是比在单个线程中运行协同例程更好吗?不一定。请记住,协同例程仍然可以在没有调度器开销的情况下实现并发—它只是管理上下文切换本身。
例如,如果你有一个例程在做一些工作,它执行一个你知道会阻塞一段时间的操作(即一个网络请求),有了协程,你可以立即切换到另一个例程,而不需要在这个决策中包括系统调度器的开销——是的,程序员必须指定协程何时可以切换。
通过大量的例程完成非常小的工作,并在彼此之间自动切换,您已经达到了任何调度器都无法企及的效率水平。您现在可以让数千个协程一起工作,而不是几十个线程。
因为你的例程现在可以在预先确定的点之间相互切换,你现在也可以避免锁定共享数据结构(因为你永远不会告诉你的代码在临界区中间切换到另一个协程)
另一个好处是更低的内存使用量。在线程模型中,每个线程都需要分配自己的堆栈,因此内存使用会随着线程数量的增加而线性增长。对于共同例程,你拥有的例程数量与你的内存使用量没有直接关系。
最后,协同例程受到了很多关注,因为在一些编程语言(如Python)中,线程不能并行运行——它们像协同例程一样并发运行,但没有低内存和免费的调度开销。
其他回答
一句话:抢占。协程就像杂耍演员一样,不断地互相传递精心排练过的分数。线程(真正的线程)几乎可以在任何时候中断,然后再恢复。当然,这也带来了各种资源冲突问题,因此才有了Python臭名昭著的GIL——全局解释器锁。
许多线程实现实际上更像协程。
首先阅读:并发和并行——有什么不同?
并发性是为分离的任务提供交错 执行。并行是同时执行多个任务 为了提高工作速度。https: / / github.com/servo/servo/wiki/Design
简单回答:对于线程,操作系统根据其调度器(操作系统内核中的一种算法)预先切换正在运行的线程。对于协程,程序员和编程语言决定何时切换协程;换句话说,通过在设置点暂停和恢复函数,任务是协作多任务的,通常(但不一定)在单个线程中。
长话长说:与由操作系统预先调度的线程相比,协程切换是协作的,这意味着程序员(可能还有编程语言及其运行时)控制切换发生的时间。
与线程相反,协程开关是抢占式的 合作(程序员控制切换发生的时间)。的 内核不涉及协同程序开关。 http: / / www.boost.org/doc/libs/1_55_0/libs/coroutine/doc/html/coroutine/overview.html
支持本机线程的语言可以将其线程(用户线程)执行到操作系统的线程(内核线程)上。每个进程至少有一个内核线程。内核线程就像进程一样,只是它们在自己的进程中与该进程中的所有其他线程共享内存空间。一个进程“拥有”它所分配的所有资源,如内存、文件句柄、套接字、设备句柄等,这些资源都在它的内核线程之间共享。
操作系统调度器是内核的一部分,它在一定时间内(在单处理器机器上)运行每个线程。调度器为每个线程分配时间(时间切片),如果线程没有在该时间内完成,调度器将抢占它(中断它并切换到另一个线程)。多个线程可以在多处理器机器上并行运行,因为每个线程可以(但不一定必须)调度到单独的处理器上。
在单处理器机器上,线程被快速地进行时间切片和抢占(在Linux上,默认的时间切片是100ms),从而使它们成为并发的。然而,它们不能并行(同时)运行,因为单核处理器一次只能运行一个程序。
协同程序和/或生成器可用于实现协作功能。它们不是在内核线程上运行并由操作系统调度,而是在单个线程中运行,直到它们屈服或结束,屈服于程序员决定的其他函数。带有生成器的语言,如Python和ECMAScript 6,可以用来构建协程。Async/await(见于c#, Python, ECMAscript 7, Rust)是建立在生成未来/承诺的生成器函数之上的抽象。
在某些上下文中,协程可能引用堆栈函数,而生成器可能引用无堆栈函数。
光纤、轻量级线程和绿色线程是协程或类似协程的东西的其他名称。它们有时看起来(通常是故意的)更像编程语言中的操作系统线程,但它们不像真正的线程那样并行运行,而是像协程那样工作。(根据语言或实现的不同,这些概念之间可能有更具体的技术特殊性或差异。)
例如,Java有“绿色线程”;这些线程是由Java虚拟机(JVM)调度的,而不是在底层操作系统的内核线程上本机调度的。它们不能并行运行,也不能利用多个处理器/核——因为那样需要一个本机线程!因为它们不是由操作系统调度的,所以它们更像协程而不是内核线程。在Java 1.2中引入本机线程之前,Java使用的是绿色线程。
Threads consume resources. In the JVM, each thread has its own stack, typically 1MB in size. 64k is the least amount of stack space allowed per thread in the JVM. The thread stack size can be configured on the command line for the JVM. Despite the name, threads are not free, due to their use resources like each thread needing its own stack, thread-local storage (if any), and the cost of thread scheduling/context-switching/CPU cache invalidation. This is part of the reason why coroutines have become popular for performance critical, highly-concurrent applications.
Mac OS只允许一个进程分配大约2000个线程,Linux为每个线程分配8MB的堆栈,并且只允许物理RAM中可以容纳的线程数量。
因此,线程的权重最重(就内存使用和上下文切换时间而言),然后是协程,最后是生成器的权重最轻。
协程是一种顺序处理的形式:在任何给定的时间只有一个在执行(就像子例程又名过程又名函数——它们只是更流畅地在彼此之间传递接力棒)。
线程(至少在概念上)是并发处理的一种形式:多个线程可以在任何给定时间执行。(传统上,在单cpu、单核机器上,并发性是在操作系统的帮助下模拟的——现在,由于很多机器都是多cpu和/或多核的,线程实际上是同时执行的,而不仅仅是“概念上”)。
这取决于你使用的语言。例如,在Lua中,它们是一样的(协程的变量类型称为线程)。
通常,协程是由程序员自行决定在哪里让步,也就是说,把控制权交给另一个例程。
相反,线程由操作系统自动管理(停止和启动),它们甚至可以同时在多核cpu上运行。
12年后的讨论,但一个协程有解释的名字。协程可以分解为Co和Routine。
在这种情况下,例程只是一系列操作/动作,通过执行/处理例程,操作序列将按照指定的完全相同的顺序逐个执行。
Co代表合作。一个协同例程被要求(或者更好地期望)自愿暂停它的执行,以给其他协同例程一个执行的机会。因此,协程是关于共享CPU资源(自愿地),以便其他人可以使用自己正在使用的相同资源。
另一方面,线程不需要挂起它的执行。挂起对线程来说是完全透明的,底层硬件迫使线程自己挂起。它对线程来说是透明的,因为它不会被通知,它的状态不会被改变,而是被保存,然后在线程被允许继续时恢复。
有一件事是不正确的,协同例程不能并发执行,竞争条件不能发生。这取决于协同例程所运行的系统,对协同例程进行成像是很容易的。
协同例程如何悬挂自己并不重要。在Windows 3.1中,int 03被编织到任何程序中(或者必须放在那里),而在c#中,我们增加了yield。
推荐文章
- Thread start()和Runnable run()有什么区别
- 如何在Python中获取线程id ?
- “Layers”和“Tiers”的区别是什么?
- 一个Java虚拟机可以支持多少线程?
- 到底什么是std::atomic?
- RxJava调度器用例
- 一般来说,在一个组件中使用一个还是多个useEffect钩子更好?
- Kotlin协程中的启动/连接和异步/等待之间有什么区别
- 何时以及如何在微服务架构中使用GraphQL
- 获取当前在Java中运行的所有线程的列表
- c#静态构造函数线程安全吗?
- CompletableFuture, Future和RxJava的Observable之间的区别
- 信号量和监视器——有什么不同?
- 同时运行多个asynctask——不可能?
- java.lang.Thread.interrupt()做什么?