什么是协程?它们与并发性有什么关系?
答案 0 :(得分:119)
协程和并发在很大程度上是正交的。协同程序是一种通用的控制结构,通过这种结构,流量控制可以在两个不同的例程之间协同传递,而不会返回。
Python中的'yield'语句就是一个很好的例子。它创造了一个协程。遇到'yield'时,保存函数的当前状态,并将控制权返回给调用函数。然后,调用函数可以将执行转移回到屈服函数,并且它的状态将恢复到遇到'yield'并继续执行的程度。
答案 1 :(得分:61)
来自Programming in Lua,“Coroutines
”部分:
一个协程类似于一个线程(在多线程意义上):它是一个执行线,有自己的堆栈,它自己的局部变量,以及它自己的指令指针;但它与其他协同程序共享全局变量和其他任何东西。线程和协同程序之间的主要区别在于,从概念上(或在字面上,在多处理器机器中),具有线程的程序并行运行多个线程。另一方面,协同程序是协作的:在任何给定时间,具有协同程序的程序仅运行其协同程序之一,并且此运行协程仅在明确请求被挂起时才暂停其执行。
所以重点是:协同程序是“协作”的。即使在多核系统中,在任何给定时间只有一个协程运行(但多个线程可以并行运行)。协同程序之间没有先发制人,正在运行的协程必须明确放弃执行。
对于“concurrency
”,您可以参考Rob Pike的slide:
并发是独立执行计算的组合。
因此在协程A执行期间,它将控制传递给协程B.然后经过一段时间后,协程B将控制权传递给协程A.因为协程之间存在依赖,它们必须运行串联,所以两个协同程序不并发。
答案 2 :(得分:23)
尽管这是一个技术问题,但我发现大多数答案都太技术化了。我很难理解协程。我有点理解,但是后来却又不同了。
我发现这里的答案非常有帮助:
https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9
引用Idan Arye的话:
要建立您的故事,我会这样写:
您开始看动画片,但这是介绍。代替 观看介绍性内容,您可以切换到游戏并进入在线大厅- 但它需要3个玩家,只有您和您的妹妹在其中。代替 等另一个玩家加入后,您会转做功课,并且 回答第一个问题。第二个问题具有指向YouTube的链接 您需要观看的视频。您打开它-它开始加载。代替 等待它加载后,您切换回卡通。简介 已经结束,所以您可以观看。现在有广告-但与此同时 第三位玩家加入,所以您切换到游戏,依此类推...
这个想法是,您不仅可以快速切换任务来完成 看起来您正在一次做所有事情。你利用时间 您正在等待事情发生(IO)做其他事情 需要您的直接关注。
一定要检查链接,还有很多我无法引用的内容。
答案 3 :(得分:11)
Coroutine类似于子程序/线程。 不同之处在于,一旦调用者调用了子例程/线程,它就永远不会返回给调用者函数。 但是协程可以在执行一段代码后返回给调用者,允许调用者执行一些自己的代码并返回到协程点,在那里它停止执行并从那里继续执行。 即。一个协程有多个入口和出口点
答案 4 :(得分:9)
基本上,有两种类型的协同程序:
Kotlin实现无堆栈协程 - 这意味着 协同程序没有自己的堆栈,因此它们不会映射到本机线程。
这些是启动协同程序的功能:
launch{}
async{}
您可以从这里了解更多信息:
https://www.kotlindevelopment.com/deep-dive-coroutines/
https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9
答案 5 :(得分:8)
我发现对此link的解释很简单。除了this answer中的最后一个要点,这些答案都没有试图解释并发与并行性。
引用,来自传奇人物乔·阿姆斯特朗(Joe Armstrong)的“编程Erlang”:
并发程序可以在并行计算机上更快地运行。
并发程序是用并发编程语言编写的程序。我们出于性能,可伸缩性或容错性的原因而编写并发程序。
并发编程语言是一种具有用于编写并发程序的显式语言构造的语言。这些构造是编程语言不可或缺的一部分,并且在所有操作系统上的行为均相同。
并行计算机是具有多个可以同时运行的处理单元(CPU或内核)的计算机。
因此,并发与并行性不同。您仍然可以在单核计算机上编写并发程序。分时调度程序会让您感觉程序正在同时运行。
并发程序有可能在并行计算机中并行运行,但不能保证。操作系统可能只给您一个核心来运行您的程序。
因此,并发是来自并发程序的软件模型,这并不意味着您的程序可以物理并行运行。
“协程”一词由两个词组成:“合作”(合作)和“例程”(功能)。
a。它实现并发还是并行?
为简单起见,让我们在单核计算机上进行讨论。
并发是通过OS中的分时实现的。线程在其分配的时间范围内在CPU内核上执行其代码。它可以被操作系统抢占。它还可能会导致对OS的控制。
另一方面,协程可以控制线程中的另一个协程,而不是OS。因此,线程中的所有协程仍会利用该线程的时间范围,而不会将CPU内核让给OS管理的其他线程。
因此,您可以想到协程由用户而不是由OS实现分时(或准并行性)。协程在分配给运行这些协程的线程的同一内核上运行。
协程是否实现并行性?如果是CPU绑定代码,则不会。像分时度假一样,您可以感觉到它们并行运行,但是它们的执行交错而不重叠。如果是IO绑定的,是的,它是通过硬件(IO设备)而不是通过代码并行实现的。
b。函数调用的区别?
如图所示,不需要调用return
来切换控制。它可以不使用return
而产生。协程在当前功能框(堆栈)上保存并共享状态。因此,它比函数轻巧得多,因为您不必保存寄存器和局部变量来堆积和call ret
时回退调用堆栈。
答案 6 :(得分:4)
另一方面,
在python gevent
库中是一个基于coroutine
的网络库,它为您提供类似异步网络请求的线程特性,而无需创建和销毁线程的开销。使用的coroutine
库是greenlet
。
答案 7 :(得分:3)
协程是一种特殊的子程序。而不是主从 调用程序和被调用子程序之间的关系与常规子程序中存在的关系一样,调用程序和被协程之间的关系更加公平。
协程是具有多个子程序的子程序 条目并对其进行控制-支持 直接在Lua中
也称为对称控制:调用方和被调用方 协程在更平等的基础上
协程调用被称为简历
协程的第一个简历是从其开始, 但随后的通话会在之后的时间点进入 协程中最后执行的语句
协程反复相互恢复,可能 永远
协程提供准并行执行 程序单位(协程);他们的执行是 交错但不重叠
答案 8 :(得分:0)
Python协程的执行可以在许多情况下暂停和恢复 点(请参见协程)。在协程函数的内部,等待 异步标识符成为保留关键字;等待表情, 与async和async with只能在协程函数中使用 身体。
协程是一种可以中止执行恢复的功能 稍后。协程是无栈的:它们通过返回暂停执行 给来电者。这允许执行顺序代码 异步(例如,无需显式处理非阻塞I / O 回调),并且还支持基于惰性计算的无限算法 序列和其他用途。
与其他人的答案进行比较:
我认为,稍后恢复部分是核心区别,就像@Twinkle一样。
尽管文档的许多字段仍在进行中,但是,此部分与大多数答案类似,除了@Nan Xiao的
另一方面,协程是协作的:在任何给定时间, 带有协程的程序仅运行其协程之一,并且 此运行的协程仅在显式运行时才挂起执行 请求被暂停。
由于它是从Lua的Program中引用的,也许与语言有关(当前不熟悉Lua),所以并非所有文档都只提到了一个部分。
与并发的关系:
Coroutines (C++20)中有一个“执行”部分。在这里引用太久了。
除了细节之外,还有几种状态。
When a coroutine begins execution
When a coroutine reaches a suspension point
When a coroutine reaches the co_return statement
If the coroutine ends with an uncaught exception
When the coroutine state is destroyed either because it terminated via co_return or uncaught exception, or because it was destroyed via its handle
作为@Adam Arold在@ user217714的回答下的评论。是并发的。
但这不同于多线程。
from std::thread
线程允许多个函数同时执行。 线程在构造后立即开始执行 关联的线程对象(在任何OS调度延迟之前),开始 在作为构造函数参数提供的顶级函数中。的 顶级函数的返回值将被忽略,如果终止 通过引发异常,将调用std :: terminate。顶层 函数可以将其返回值或异常传达给 通过std :: promise或通过修改共享变量(可能会 需要同步,请参见std :: mutex和std :: atomic)
由于它是并发的,因此它就像多线程一样工作,尤其是在不可避免的情况下(从OS的角度来看),这也是造成混乱的原因。
答案 9 :(得分:0)
我将扩展@ user21714的答案。协程是不能同时运行的独立执行路径。它们依靠控制器(例如python
控制器库)来处理这些路径之间的切换。但是,为此,协程本身需要调用yield
或类似的结构,以使其暂停执行。
线程在独立的计算资源上运行,并且彼此并行运行。由于它们使用不同的资源,因此不需要调用 yield 来允许其他执行路径继续进行。
您可以通过启动一个多重读取的程序来看到这种效果-例如一个jvm
应用程序-其中所有八个core i7
超线程内核都被利用:您可能会在Activity Monitor
或Top
中看到797%的利用率。相反,在运行典型的python
程序时-甚至使用coroutines
或python threading
的程序-利用率将达到100%。即一个计算机超线程。
答案 10 :(得分:0)
通常我们会喜欢-协程是轻量级的线程,它们使我们能够以同步方式编写异步,非阻塞代码
关于Kotlin协程:
协程是一个合成糖/附加层,可让您以非阻塞方式和无回调来运行大型任务。协程包含一些类(Job
,Dispatcher
,Scope
,Builder
)和body
它使用Continuation
函数创建state machine
类invokeSuspend()
suspended
函数只是使用Continuation
的标记。
coroutine
的行为完全取决于库实现的要点