什么是协程?它们与并发性有什么关系?
当前回答
我将详述@user21714的答案。协程是独立的执行路径,不能同时运行。它们依赖于一个控制器(例如python控制器库)来处理这些路径之间的切换。但是为了实现这一点,协同程序本身需要调用yield或类似的结构,以允许它们的执行暂停。
相反,线程运行在独立的计算资源上,并且彼此并行。由于它们位于不同的资源上,因此不需要调用yield来允许其他执行路径继续进行。
您可以通过启动一个多线程程序(例如jvm应用程序)来看到这种效果,其中所有八个核心i7超线程核心都被利用了:您可能会在Activity Monitor或Top中看到797%的利用率。相反,当运行一个典型的python程序(即使是带有协程或python线程的程序)时,利用率最高将达到100%。例如,一台机器超线程。
其他回答
来自Python Coroutine:
Python协程的执行可以在许多地方暂停和恢复 点(见协程)。在协程函数体内,等待 异步标识符成为保留关键字;等待表达式, Async for和Async with只能在协程函数中使用 的身体。
From Coroutines (c++ 20)
协程是一种可以挂起要恢复的执行的函数 以后。协程是无堆栈的:它们通过返回来暂停执行 给打电话的人。这允许执行顺序代码 异步(例如,不显式地处理非阻塞I/O 回调),并且还支持惰性计算无限上的算法 序列和其他用途。
与他人的答案比较:
在我看来,后面的恢复部分是一个核心的区别,就像@Twinkle一样。 虽然文件的很多方面还在完善中,但是这一部分和大部分的答案是相似的,除了@南晓
另一方面,协程是协作的:在任何给定的时间,一个 带有协程的程序只运行其中一个协程,并且 这个正在运行的协程仅在显式执行时才会暂停执行 请求被暂停。
因为引用自Program in Lua,可能是语言相关(目前不熟悉Lua),不是所有文档都只提到一个部分。
与concurrent的关系: 协程(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::线程
Threads allow multiple functions to execute concurrently. Threads begin execution immediately upon construction of the associated thread object (pending any OS scheduling delays), starting at the top-level function provided as a constructor argument. The return value of the top-level function is ignored and if it terminates by throwing an exception, std::terminate is called. The top-level function may communicate its return value or an exception to the caller via std::promise or by modifying shared variables (which may require synchronization, see std::mutex and std::atomic)
因为它是并发的,它就像多线程一样工作,特别是当等待是不可避免的(从操作系统的角度来看),这也是为什么它令人困惑的原因。
在Lua编程中,“协程”部分:
A coroutine is similar to a thread (in the sense of multithreading): it is a line of execution, with its own stack, its own local variables, and its own instruction pointer; but it shares global variables and mostly anything else with other coroutines. The main difference between threads and coroutines is that, conceptually (or literally, in a multiprocessor machine), a program with threads runs several threads in parallel. Coroutines, on the other hand, are collaborative: at any given time, a program with coroutines is running only one of its coroutines, and this running coroutine suspends its execution only when it explicitly requests to be suspended.
所以关键是:协程是“协作的”。即使在多核系统中,也只有一个协程在任何给定时间运行(但多个线程可以并行运行)。协程之间存在不可抢占性,运行中的协程必须显式放弃执行。
关于“并发性”,你可以参考Rob Pike的幻灯片:
并发是独立执行计算的组合。
所以在协程A的执行过程中,它把控制权传递给了协程B。经过一段时间后,协程B又把控制权传递给了协程A。由于协程之间存在依赖关系,它们必须串联运行,所以这两个协程不是并发的。
另一方面, 在python中,gevent库是一个基于协程的网络库,它为您提供线程类功能,如异步网络请求,而无需创建和销毁线程的开销。所使用的协程库是绿色的。
我将详述@user21714的答案。协程是独立的执行路径,不能同时运行。它们依赖于一个控制器(例如python控制器库)来处理这些路径之间的切换。但是为了实现这一点,协同程序本身需要调用yield或类似的结构,以允许它们的执行暂停。
相反,线程运行在独立的计算资源上,并且彼此并行。由于它们位于不同的资源上,因此不需要调用yield来允许其他执行路径继续进行。
您可以通过启动一个多线程程序(例如jvm应用程序)来看到这种效果,其中所有八个核心i7超线程核心都被利用了:您可能会在Activity Monitor或Top中看到797%的利用率。相反,当运行一个典型的python程序(即使是带有协程或python线程的程序)时,利用率最高将达到100%。例如,一台机器超线程。
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调用
协程的行为完全依赖于库的实现