Kotlin中线程和协程之间的区别

Jem*_*ili 55 kotlin kotlin-coroutines

Kotlin中是否有任何特定的语言实现,它与协同程序的另一种语言实现有所区别?

  • coroutine就像轻量级线程一样意味着什么?
  • 有什么不同?
  • kotlin协程实际上并行/同时运行吗?
  • 即使在多核系统中,在任何给定时间只有一个协程运行(是不是?)

在这里,我将启动100000个协程,这段代码背后会发生什么?

for(i in 0..100000){
   async(CommonPool){
    //run long running operations
  }
}
Run Code Online (Sandbox Code Playgroud)

Rom*_*rov 56

coroutine就像轻量级线程一样意味着什么?

与线程一样,Coroutine表示与其他协同程序(线程)同时执行的一系列操作.

有什么不同?

线程直接链接到相应OS(操作系统)中的本机线程,并消耗大量资源.特别是,它为其堆栈消耗了大量内存.这就是为什么你不能只创建100k线程.你可能会耗尽内存.线程之间的切换涉及OS内核调度程序,就消耗的CPU周期而言,这是一项非常昂贵的操作.

另一方面,协程纯粹是用户级语言抽象.它不会绑定任何本机资源,并且在最简单的情况下,它只使用JVM堆中的一个相对较小的对象.这就是为什么很容易创建100k协同程序.在协程之间切换根本不涉及OS内核.它可以像调用常规函数一样便宜.

kotlin的协同程序实际上并行/同时运行吗?即使在多核系统中,在任何给定时间只有一个协程运行(是不是?)

一个协程可以运行或暂停.挂起的协程没有与任何特定线程相关联,但是正在运行的协程在某个线程上运行(使用线程是在OS进程内执行任何操作的唯一方法).是否所有运行在同一线程上的协同程序(因此可能只在多核系统中使用单个CPU)或在不同的线程中运行(因此可能使用多个CPU)完全由使用协同程序的程序员掌握.

在Kotlin中,协同程序的调度通过协程上下文来控制.您可以在"kotlinx.coroutines指南"中阅读更多相关内容

在这里,我将启动100000个协程,这段代码背后会发生什么?

假设您正在使用项目中的launch函数和CommonPool上下文kotlinx.coroutines(这是开源的),您可以在此处检查它们的源代码:

launch刚刚创造了新的协同程序,同时CommonPool调度的协同程序到ForkJoinPool.commonPool()其确实使用多个线程,因此在本例中多个CPU执行.

launch调用之后的代码{...}称为挂起lambda.它是什么以及如何暂停lambdas和函数实现(编译)以及标准库函数和类之类startCoroutines,suspendCoroutineCoroutineContext在相应的Kotlin协程设计文档中进行了解释.

  • 这与线程没有什么不同.如果线程数大于物理核心数,则不是真正的并行性.不同之处在于线程安排在核心_preemptively_上,而协程安排在线程上_cooperatively_ (4认同)
  • 粗略地说,这是否意味着启动一个couroutine类似于将一个作业添加到线程队列中,其中线程队列由用户控制? (3认同)
  • 是的。它可以是单个线程的队列,也可以是线程池的队列。您可以将协程视为更高级别的原语,它可以让您避免手动(重新)向队列提交业务逻辑的延续。 (2认同)
  • 那么这是否意味着当我们并行运行多个协程时,如果协程的数量远大于队列中线程的数量,那么这不是真正的并行?如果是这样的话,那么这听起来真的很像Java的`Executor`,这两者之间有什么关系吗? (2认同)
  • 第二和第三链接已损坏(404)。 (2认同)

Rus*_*lan 47

由于我只在JVM上使用协同程序,我将讨论JVM后端,还有Kotlin Native和Kotlin JavaScript,但Kotlin的这些后端超出了我的范围.

让我们首先将Kotlin协程与其他语言协程进行比较.基本上,你应该知道有两种类型的协同程序:无堆栈和堆栈.Kotlin实现了无堆栈协程 - 这意味着协程没有自己的堆栈,这限制了协同程序可以做的一点点.你可以在这里阅读一个很好的解释.

例子:

  • Stackless:C#,Scala,Kotlin
  • Stackful:Quasar,Javaflow

coroutine就像轻量级线程一样意味着什么?

这意味着Kotlin中的协程没有自己的堆栈,它不映射到本机线程,它不需要在处理器上进行上下文切换.

有什么不同?

线程 - 抢先式多任务处理.(通常).协同 - 合作多任务.

线程 - 由OS(通常)管理.协同程序 - 由用户管理.

kotlin的协程实际上并行/同时运行吗?

这取决于你可以在自己的线程中运行每个协同程序,或者你可以在一个线程或一些固定的线程池中运行所有协同程序.

更多关于coroutines如何在这里执行.

即使在多核系统中,在任何给定时间只有一个协程运行(是不是?)

不,请看上一个答案.

在这里,我将启动100000个协程,这段代码背后会发生什么?

实际上,这取决于.但假设您编写以下代码:

fun main(args: Array<String>) {
    for (i in 0..100000) {
        async(CommonPool) {
            delay(1000)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此代码立即执行.

因为我们需要等待async来电的结果.

所以让我们解决这个问题:

fun main(args: Array<String>) = runBlocking {
    for (i in 0..100000) {
        val job = async(CommonPool) {
            delay(1)
            println(i)
        }

        job.join()
    }
}
Run Code Online (Sandbox Code Playgroud)

运行此程序时,kotlin将创建2*100000个实例Continuation,这将占用几十Mb的RAM,在控制台中,您将看到1到100000的数字.

所以让我们以这种方式重写这段代码:

fun main(args: Array<String>) = runBlocking {

    val job = async(CommonPool) {
        for (i in 0..100000) {
            delay(1)
            println(i)
        }
    }

    job.join()
}
Run Code Online (Sandbox Code Playgroud)

我们现在取得了什么?现在我们只创建了100001个实例Continuation,这要好得多.

每个创建的Continuation都将在CommonPool(这是ForkJoinPool的静态实例)上调度和执行.

  • 很好的答案,但我建议做一个重要的修正.Kotlin中的协程_在初始预发布预览中用于无堆叠,但实际上是在Kotlin 1.1中发布的,支持任何堆叠深度的悬浮,就像在Quasar中一样.对于那些熟悉Quasar的人来说,很容易看到Quasar的`throws SuspendExecution`和Kotlin的`suspend`修饰符之间的一对一对应关系.当然,实现细节是完全不同的,但用户体验非常相似. (12认同)
  • 我将以下列方式对各种语言的协程进行分类:C#,JS等具有基于_future/promise的coroutines_.这些语言中的任何异步计算都必须返回某种类似未来的对象.把它们称为无堆栈是不公平的.你_can_表达任何深度的异步计算,它只是在语法和实现方面效率低下.Kotlin,Quasar等都有_suspension/continuation-based coroutines_.它们更加强大,因为它们可以与未来类似的对象一起使用,也可以不使用它们,仅使用挂起功能. (8认同)
  • 也欢迎您签出相应的[设计文件]在实际执行科特林协同程序的细节(https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md). (5认同)
  • 好.这是一篇很好的论文,给出了协程的背景,并给出了"堆栈协程"的或多或少精确的定义:http://www.inf.puc-rio.br/~roberto/docs/MCC15-04.pdf它意味着Kotlin实现_stackful coroutines_. (4认同)
  • 坦率地说,我不知道"堆叠协程"这个词是什么意思.我没有看到这个术语的任何正式/技术定义,我看到不同的人以完全矛盾的方式使用它.我完全避免使用术语"堆叠协程".我可以肯定地说,而且很容易验证的是,Kotlin协程与Quasar更接近并且与C#不同.无论你对"堆叠协同程序"一词的具体定义如何,将Kotlin corutines放入与C#异步相同的bin中似乎并不正确. (3认同)