什么是协程?


204

什么是协程?它们与并发有何关系?


2
并发代码不一定必须以“并行”方式运行(不必引入新术语)。
lucid_dreamer

2
我用标准C编写了一个协程库,支持针对Linux,BSD和Windows的select / poll / eplll / kqueue / iocp / Win GUI消息。这是github.com/acl-dev/libfiber中的一个开源项目。咨询会很好。
郑树新

更多有趣的信息在这里:stackoverflow.com/q/16951904/14357
消费者

我可以想象,如果在当前时代提出这个问题,这个问题将被否决。不知道为什么与以前相比,社区观念有如此巨大的差异?
tnkh

一个协程是可以达到的回报之前,暂停其执行的功能,它可以间接地控制传递给另一个协程一段时间。
hassanzadeh.sd

Answers:


138

协程和并发在很大程度上是正交的。协程是一种通用的控制结构,流控制在两个不同的例程之间协同传递而不返回。

Python中的“ yield”语句就是一个很好的例子。它创建一个协程。遇到“ yield”时,将保存函数的当前状态,并将控制权返回给调用函数。然后,调用函数可以将执行转移回给yield函数,并且其状态将恢复到遇到“ yield”的点,并且执行将继续。


19
将函数包装到协程中直接调用函数和从协程中产生有什么区别?
Ming Li

3
也许最好解释一下,在这种情况下,这两个概念并不是真正的“正交”。您绝对可以得出两个概念是如何相似的。在两个或多个事物之间传递控制的想法非常相似。
steviejay

8
Coroutines are a general control structure whereby flow control is cooperatively passed between two different routines without returning.<-这并发。您要查找的单词是并行性。
亚当·阿罗德

@steviejay orthogonal = Not similar to each other吗?
tonix

1
@tonix告诉我,orthogonal意思是“彼此独立”。
瑞克

77

在Lua编程Coroutines”部分:

协程类似于线程(在多线程意义上):它是一条执行线,具有自己的堆栈,自己的局部变量和自己的指令指针;但是它与其他协程共享全局变量,并且几乎共享其他任何变量。线程和协程之间的主要区别在于,从概念上(或从字面上看,在多处理器计算机中),具有线程的程序并行运行多个线程。另一方面,协程是协作的:在任何给定时间,具有协程的程序仅运行其协程中的一个,并且该运行的协程仅在显式请求暂停时才暂停其执行。

关键是:协程是“协作的”。即使在多核系统中,任何给定时间都只有一个协程正在运行(但是多个线程可以并行运行)。协程之间是非抢占式的,正在运行的协程必须显式放弃执行。

对于“ concurrency”,您可以参考Rob Pike的幻灯片

并发是独立执行计算的组成。

因此,在协程A执行期间,它会将控制权交给协程B。然后,一段时间后,协程B将控制权交给协程A。由于协程之间存在依赖关系,它们必须串联运行,因此两个协程不是并发的


6
协程不是独立执行的。他们轮流,彼此等待对方完成部分工作。他们积极地相互配合。这与Rob Pikes的并发定义相反。
Erick G. Hagstrom

2
@ ErickG.Hagstrom:尽管它们不是独立执行的,但是每个协程的逻辑都可以独立,对吗?如果这是正确的,则就像在单核CPU上运行的非抢占式OS一样,一个进程必须放弃CPU才能运行其他任务。

6
放弃CPU来运行其他任务与告诉某些特定其他进程该执行该命令之间是有区别的。协程会做后者。这在任何意义上都不是独立的。
Erick G. Hagstrom

7
@ChrisClark我同意你的看法。协程是并发的。以下是维基百科的一些引文:协程与线程非常相似。但是,协程是协作式多任务的,而线程通常是抢先式多任务的。这意味着它们提供并发性,但不提供并行性
smwikipedia

3
并且:协作式多任务处理,也称为非抢先式多任务处理,是一种计算机多任务处理方式,其中操作系统从不启动从正在运行的进程到另一个进程的上下文切换。取而代之的是,进程会定期或在空闲时或在逻辑上受阻时自动产生控制权,以使多个应用程序可以同时运行。
smwikipedia

47

尽管这是一个技术问题,但我发现大多数答案都过于技术化。我很难理解协程。我有点理解,但是后来却又不同了。

我发现这里的答案非常有帮助:

https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9

引用Idan Arye的话:

为了建立您的故事,我会这样说:

您开始看动画片,但这是介绍。无需观看介绍视频,而是切换到游戏并进入在线大厅-但它需要3个玩家,并且只有您和您的妹妹在其中。无需等待其他玩家加入,而是切换到家庭作业,然后回答第一个问题。第二个问题有指向您需要观看的YouTube视频的链接。您打开它-它开始加载。无需等待加载,您可以切换回卡通。简介已经结束,因此您可以观看。现在有商业广告-但与此同时有第三位玩家加入,所以您切换到游戏,依此类推...

这样做的想法是,您不只是快速切换任务就可以让您看起来好像一次完成所有任务。您可以利用等待事件发生的时间(IO)来做其他需要您直接注意的事情。

绝对检查链接,还有更多我无法引用的内容。


6
非常简单明了的插图。为此+1。
塔斯林·奥塞尼

很好的例证。我建立了一个类似的故事-排队等候收集包裹。但是今天,您的现实得多,当送货上门时,谁在排队?哈哈
阿波拉克

1
真棒。从报价本身来看,这非常清楚。
Farruh Habibullaev,

15

协程类似于子例程/线程。区别在于,一旦调用者调用了子例程/线程,它将永远不会返回到调用者函数。但是协程可以在执行几段代码后返回到调用方,从而使调用方可以执行自己的一些代码,并返回到协程点,在此停止执行并从此处继续。即。一个协程有多个入口和出口点


它与线程不太相似-线程独立且同时运行(并行的独立内核)。另外,子例程比较在存在多个独立的执行路径并且它们不会相互返回结果的意义上失败。
javadba

11
  • 协程是Kotlin语言提供的强大功能
  • 协程是一种编写异步,非阻塞代码(甚至更多)的新方法
  • 协程是轻量级的线程。轻量级线程意味着它不映射在本机线程上,因此它不需要在处理器上进行上下文切换,因此它们更快。
  • 它不映射在本机线程上
  • 协程和线程都是多任务的。但是不同之处在于,线程是由操作系统管理的,而协程是由用户管理的。

基本上,协程有两种类型:

  1. 无堆叠
  2. 堆积如山

Kotlin实现了无堆栈的协程—这意味着协程没有自己的堆栈,因此它们不映射在本机线程上。

这些是启动协程的功能:

launch{}

async{}

您可以从这里了解更多信息:

https://www.kotlindevelopment.com/deep-dive-coroutines/

https://blog.mindorks.com/what-are-coroutines-in-kotlin-bf4fecd476e9


1
好答案!对Kotlin和Android开发人员有用。
Malwinder Singh,

5

值得一提的是,python gevent库中有一个coroutine基于网络的库,该库为您提供类似于线程的功能,例如异步网络请求,而无需创建和销毁线程。使用的coroutine库是greenlet


2

Python协程

Python协程的执行可以在许多地方暂停和恢复(请参阅协程)。在协程函数的主体内部,await和async标识符成为保留关键字。await表达式,for和async with只能在协程函数体中使用。

协程(C ++ 20)

一个协同程序是可以暂停执行中的功能恢复 以后。协程是无堆栈的:它们通过返回到调用方来挂起执行。这允许异步执行的顺序代码(例如,在没有显式回调的情况下处理非阻塞I / O),并且还支持延迟计算的无限序列和其他用途的算法。

与其他人的答案比较:

我认为,后面的续集是一个核心区别,就像@Twinkle一样。
尽管文档的许多字段仍在进行中,但是,此部分与大多数答案类似,除了@Nan Xiao的

另一方面,协程是协作的:在任何给定时间,具有协程的程序仅在运行其协程中的一个,并且该运行的协程仅在显式请求暂停时才暂停其执行。

由于它是从Lua的Program中引用的,所以它可能与语言有关(目前还不熟悉Lua),因此并非所有文档都只提到了一部分。

与并发的关系:协程(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的回答下的评论。它是并发的。
但这不同于多线程。 来自std :: thread

线程允许多个功能同时执行。线程在构造关联的线程对象(等待任何OS调度延迟)后立即开始执行,从作为构造函数参数提供的顶级函数开始。顶级函数的返回值将被忽略,如果它通过引发异常终止,则将调用std :: terminate。顶级函数可以通过std :: promise或通过修改共享变量(可能需要同步,请参见std :: mutex和std :: atomic)将其返回值或异常传达给调用方。

由于它是并发的,因此它就像多线程一样工作,尤其是在不可避免的情况下(从OS的角度来看),这就是为什么它令人困惑。


1

协程是一种特殊的子程序。与常规子程序中存在的调用方和被调用子程序之间的主从关系相比,调用方和被协程更公平。

  • 协程是一个子程序,具有多个条目并对其进行控制-在Lua中直接支持

  • 也称为对称控制:调用方和协程在更平等的基础上

  • 协程调用称为简历

  • 协程的第一次恢复是从其开始,但是随后的调用在协程中最后一个执行的语句之后的点进入

  • 协程反复出现,可能永远

  • 协程提供程序单元(协程)的准并行执行;它们的执行是交错的,但不能重叠

例1 例2


1

我发现此链接的解释很简单。除了该答案的最后一个要点之外,这些答案均未尝试解释并发与并行性。

  1. 什么是并发(程序)?

乔·阿姆斯特朗(Joe Armstrong)在“编程Erlang”一书中引用了以下传奇人物:

并发程序可以在并行计算机上更快地运行。

  • 并发程序是用并发编程语言编写的程序。我们出于性能,可伸缩性或容错性的原因而编写并发程序。

  • 并发编程语言是一种具有用于编写并发程序的显式语言构造的语言。这些构造是编程语言的组成部分,并且在所有操作系统上的行为均相同。

  • 并行计算机是具有多个可以同时运行的处理单元(CPU或内核)的计算机。

因此,并发与并行性并不相同。您仍然可以在单核计算机上编写并发程序。分时调度程序会让您感觉程序正在同时运行。

并发程序有可能在并行计算机中并行运行,但不能保证。操作系统可能只给您一个核心来运行您的程序。

因此,并发是来自并发程序的软件模型,并不意味着您的程序可以物理并行运行。

  1. 协程和并发

“协程”一词由两个词组成:“合作”(合作)和“例程”(功能)。

一个。它实现并发还是并行?

简单来说,让我们在单核上进行讨论计算机。

并发是通过OS的分时实现的。线程在其分配的时间范围内在CPU内核上执行其代码。它可以被操作系统抢占。它还可能会控制OS。

另一方面,协程可以控制线程中的另一个协程,而不是OS。因此,线程中的所有协程仍会利用该线程的时间范围,而不会将CPU内核让给OS管理的其他线程。

因此,您可以想到协程是由用户而不是由OS(或准并行性)实现的时分。协程在分配给运行这些协程的线程的同一内核上运行。

协程是否达到并行性?如果是CPU绑定代码,则不会。就像分时度假一样,您可以感觉到它们并行运行,但是它们的执行交错​​而不重叠。如果是IO绑定的,是的,它是通过硬件(IO设备)而不是通过代码并行实现的。

b。函数调用的区别?

在此处输入图片说明

如图所示,不需要调用return即可切换控件。没有可以屈服return。协程在当前功能框(堆栈)上保存并共享状态。因此,它比函数轻巧得多,因为您不必保存寄存器和局部变量以在时堆叠和回退调用栈call ret


0

我将扩展@ user21714的答案。协程是不能同时运行的独立执行路径。它们依靠控制器(例如python控制器库)来处理这些路径之间的切换。但是,为了做到这一点,协程本身需要调用yield要使或类似的结构来暂停其执行。

相反,线程在独立的计算资源上运行并且彼此并行。由于它们使用不同的资源,因此无需调用yield即可继续执行其他执行路径。

您可以通过启动一个多线程程序(例如,一个jvm应用程序)来看到这种效果,其中所有8个core i7超线程内核都在其中被利用:在Activity Monitor或中,您可能会看到797%的利用率Top。相反,在运行典型python程序(甚至使用coroutinespython threading)时,利用率将达到100%。即台机器超线程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.