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


当前回答

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调用

协程的行为完全依赖于库的实现

其他回答

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

协程作为并发性的实现和多线程的替代方案。

协程是实现并发的单线程解决方案。

         A-Start ------------------------------------------ A-End   
           | B-Start -----------------------------------------|--- B-End   
           |    |      C-Start ------------------- C-End      |      |   
           |    |       |                           |         |      |
           V    V       V                           V         V      V      
1 thread->|<-A-|<--B---|<-C-|-A-|-C-|--A--|-B-|--C-->|---A---->|--B-->| 

与多线程解决方案相比:

thread A->|<--A|          |--A-->|
thread B------>|<--B|            |--B-->|
thread C ---------->|<---C|             |C--->|

协程是异步编程的一种实现,异步编程用于实现并发。 许多语言使用协程实现异步编程。其他答案表明Python, Kotlin, Lua, c++已经做到了。 最有用/通常用于涉及I/O绑定问题的场景,例如在获取数据时呈现UI,或从多个数据源下载。

另一方面, 在python中,gevent库是一个基于协程的网络库,它为您提供线程类功能,如异步网络请求,而无需创建和销毁线程的开销。所使用的协程库是绿色的。

来自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)

因为它是并发的,它就像多线程一样工作,特别是当等待是不可避免的(从操作系统的角度来看),这也是为什么它令人困惑的原因。

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

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