什么是协程?它们与并发性有什么关系?
当前回答
我发现这个链接的解释非常直接。这些答案中没有一个试图解释并发和并行,除了这个答案中的最后一个要点。
什么是并发(程序)?
引用自Joe Armstrong的“编程Erlang”,传奇人物:
并发程序在并行计算机上可能运行得更快。
a concurrent program is a program written in a concurrent programming language. We write concurrent programs for reasons of performance, scalability, or fault tolerance. a concurrent programming language is a language that has explicit language constructs for writing concurrent programs. These constructs are an integral part of programming language and behave the same way on all operating systems. a parallel computer is a computer that has several processing units (CPUs or cores) that can run at the same time.
所以并发性和并行性是不同的。您仍然可以在单核计算机上编写并发程序。分时调度器会让你感觉你的程序正在并发运行。
并发程序有可能在并行计算机中并行运行,但不能保证。操作系统可能只给你一个内核来运行程序。
因此,并发是一个来自并发程序的软件模型,这并不意味着您的程序可以在物理上并行运行。
协程和并发
“协程”一词由两个词组成:“co”(合作)和“routine”(函数)。
A.它实现了并发还是并行?
为了简单起见,让我们在单核计算机上讨论它。
并发性是通过来自操作系统的分时实现的。线程在CPU内核上按照指定的时间框架执行代码。它可以被OS抢占。它也可以将控制权交给操作系统。
另一方面,协程将控制权交给线程内的另一个协程,而不是交给OS。因此,线程内的所有协程仍然利用该线程的时间框架,而不会将CPU核心交给由操作系统管理的其他线程。
因此,您可以认为协程是通过用户而不是通过操作系统(或准并行)实现分时的。协程运行在分配给运行这些协程的线程的同一个核心上。
协程能实现并行吗?如果它是cpu绑定的代码,则不会。就像分时系统一样,它让你感觉它们是平行运行的,但它们的执行是交错的,而不是重叠的。如果它是IO绑定的,是的,它通过硬件(IO设备)实现并行,而不是通过您的代码。
B.与函数调用的区别?
如图所示,它不需要调用return来切换控制。它可以不计回报地付出。协程保存并共享当前函数帧(堆栈)上的状态。因此,它比函数轻量级得多,因为你不需要保存寄存器和局部变量来堆栈,并在调用ret时倒带调用stack。
其他回答
Kotlin 协程
[同步与异步]
[并发vs并行]
通常我们认为协程是轻量级线程,它们允许我们以同步的方式编写异步的、非阻塞的代码
至于Kotlin协程:
协程是一个合成糖/附加层,它允许你以非阻塞的方式运行任务,没有回调。协程由以下组件组成:
延续 作业-管理程序和保存它的状态 CoroutineScope(包含作业和Xontext)——处理作业组(层次结构)并管理它们(启动和取消) Dispatcher -用于处理线程池- Main, IO, Default… 上下文-像Map这样的数据结构,以保存一些日期,如Job, Dispatcher和自定义数据
让我们回顾一些例子
class MyClass {
val network = Network()
val fileSystem = FileSystem()
suspend fun downloadFile(): File {
//suspendCoroutine is key point
return suspendCoroutine { continuation ->
network.download(callback: Network.Callback {
override fun onSuccess(file: File) {
continuation.resume(file)
}
})
}
}
suspend fun saveFile(file: File) {
//suspendCoroutine is key point
return suspendCoroutine { continuation ->
fileSystem.save(callback: FileSystem.Callback {
override fun onSuccess() {
continuation.resume()
}
})
}
}
GlobalScope.launch {
val downloadResult = downloadFile() //1. suspend function
show(downloadResult) //2. UI
saveFile(downloadResult) //3. suspend function
}
延续
它创建了Continuation类,它是一个状态机,内部有invokeSuspend()函数。在任何挂起函数的末尾调用invokeSuspend()(像回调一样)
class Continuation {
int label;
//block of local variabels
File file;
void invokeSuspend(Object result) {
switch (label) {
case 0: {
label = 1;
downloadFile(this); //1. suspend function
return;
}
case 1: {
file = (File) result; //work with result
show(file); //2. UI
saveFile(file, this); //3.suspend function
return;
}
}
}
}
class MyClass {
fun downloadFile(continuation: Continuation): File {
//logic
continuation.invokeSuspend(file)
}
fun saveFile(file: File, continuation: Continuation) {
//logic
continuation.invokeSuspend()
}
}
暂停
suspended只是一个函数的标记,它将被添加新的延续参数(continue: continue) 分割状态机,这意味着它可以暂停机器 暂停函数应该在Continuation.resume() -> Continuation. invokesuspend()中调用Continuation.resume() 挂起的函数只能从couroutine调用
协程的行为完全依赖于库的实现
我将详述@user21714的答案。协程是独立的执行路径,不能同时运行。它们依赖于一个控制器(例如python控制器库)来处理这些路径之间的切换。但是为了实现这一点,协同程序本身需要调用yield或类似的结构,以允许它们的执行暂停。
相反,线程运行在独立的计算资源上,并且彼此并行。由于它们位于不同的资源上,因此不需要调用yield来允许其他执行路径继续进行。
您可以通过启动一个多线程程序(例如jvm应用程序)来看到这种效果,其中所有八个核心i7超线程核心都被利用了:您可能会在Activity Monitor或Top中看到797%的利用率。相反,当运行一个典型的python程序(即使是带有协程或python线程的程序)时,利用率最高将达到100%。例如,一台机器超线程。
协程类似于子例程/线程。 区别在于,一旦调用方调用了子例程/线程,它将永远不会返回到调用方函数。 但是协程可以在执行几段代码后返回到调用方,允许调用方执行一些自己的代码,并返回到它停止执行的协程点,并从那里继续。 ie。协程有多个入口点和出口点
另一方面, 在python中,gevent库是一个基于协程的网络库,它为您提供线程类功能,如异步网络请求,而无需创建和销毁线程的开销。所使用的协程库是绿色的。
我发现大多数答案都太专业了,尽管这是一个技术问题。我很难理解协同程序的过程。我有点明白,但我不能同时明白。
我发现这个答案非常有用:
https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9
引用伊丹·阿耶的话:
To build on your story, I'd put it something like this: You start watching the cartoon, but it's the intro. Instead of watching the intro you switch to the game and enter the online lobby - but it needs 3 players and only you and your sister are in it. Instead of waiting for another player to join you switch to your homework, and answer the first question. The second question has a link to a YouTube video you need to watch. You open it - and it starts loading. Instead of waiting for it to load, you switch back to the cartoon. The intro is over, so you can watch. Now there are commercials - but meanwhile a third player has joined so you switch to the game And so on... The idea is that you don't just switch the tasks really fast to make it look like you are doing everything at once. You utilize the time you are waiting for something to happen(IO) to do other things that do require your direct attention.
一定要检查链接,还有更多我不能引用的东西。