简体   繁体   English

Kotlin 中线程和协程的区别

[英]Difference between thread and coroutine in Kotlin

Is there any specific language implementation in Kotlin, which differs it from another languages implementation of coroutines? Kotlin 中是否有任何特定的语言实现,它与协程的其他语言实现不同?

  • What means that coroutine is like light-weight thread?什么意味着协程就像轻量级线程?
  • What is the difference?有什么不同?
  • Are kotlin coroutines actually running in parallely / concurrently? kotlin 协程实际上是并行/并发运行的吗?
  • Even in multi-core system, there is only one coroutine running at any given time (is it right?)即使在多核系统中,在任何给定时间也只有一个协程在运行(对吗?)

Here I'm starting 100000 coroutines, what happens behind this code?这里我启动了 100000 个协程,这段代码背后发生了什么?

for(i in 0..100000){
   async(CommonPool){
    //run long running operations
  }
}

What means that coroutine is like light-weight thread?什么意味着协程就像轻量级线程?

Coroutine, like a thread, represents a sequence of actions that are executed concurrently with other coroutines (threads).协程就像一个线程,代表一系列与其他协程(线程)同时执行的动作。

What is the difference?有什么不同?

A thread is directly linked to the native thread in the corresponding OS (operating system) and consumes a considerable amount of resources.一个线程直接链接到相应OS(操作系统)中的本地线程,消耗相当多的资源。 In particular, it consumes a lot of memory for its stack.特别是,它的堆栈消耗了大量内存。 That is why you cannot just create 100k threads.这就是为什么你不能只创建 100k 个线程。 You are likely to run out of memory.您可能会耗尽内存。 Switching between threads involves OS kernel dispatcher and it is a pretty expensive operation in terms of CPU cycles consumed.线程之间的切换涉及操作系统内核调度程序,就消耗的 CPU 周期而言,这是一项非常昂贵的操作。

A coroutine, on the other hand, is purely a user-level language abstraction.另一方面,协程纯粹是用户级语言抽象。 It does not tie any native resources and, in the simplest case, uses just one relatively small object in the JVM heap.它不绑定任何本机资源,在最简单的情况下,它只使用 JVM 堆中一个相对较小的对象。 That is why it is easy to create 100k coroutines.这就是为什么创建 10 万个协程很容易。 Switching between coroutines does not involve OS kernel at all.在协程之间切换根本不涉及操作系统内核。 It can be as cheap as invoking a regular function.它可以像调用常规函数一样便宜。

Are kotlin's coroutines actually running in parallely / concurrently? kotlin 的协程实际上是并行/并发运行的吗? Even in multi-core system, there is only one coroutine running at any given time (is it right?)即使在多核系统中,在任何给定时间也只有一个协程在运行(对吗?)

A coroutine can be either running or suspended.协程可以运行或暂停。 A suspended coroutine is not associated to any particular thread, but a running coroutine runs on some thread (using a thread is the only way to execute anything inside an OS process).挂起的协程与任何特定线程无关,但正在运行的协程在某个线程上运行(使用线程是在 OS 进程内执行任何内容的唯一方法)。 Whether different coroutines all run on the same thread (a thus may use only a single CPU in a multicore system) or in different threads (and thus may use multiple CPUs) is purely in the hands of a programmer who is using coroutines.不同的协程是否都运行在同一个线程上(因此在多核系统中可能只使用一个 CPU)还是在不同的线程中(因此可能使用多个 CPU)完全取决于使用协程的程序员。

In Kotlin, dispatching of coroutines is controlled via coroutine context .在 Kotlin 中,协程的调度是通过协程上下文控制的。 You can read more about then in the Guide to kotlinx.coroutines您可以在kotlinx.coroutines 指南中阅读更多相关信息

Here I'm starting 100000 coroutines, what happens behind this code?这里我启动了 100000 个协程,这段代码背后发生了什么?

Assuming that you are using launch function and CommonPool context from the kotlinx.coroutines project (which is open source) you can examine their source code here:假设您正在使用kotlinx.coroutines项目(开源)中的launch函数和CommonPool上下文,您可以在此处检查它们的源代码:

The launch just creates new coroutine, while CommonPool dispatches coroutines to a ForkJoinPool.commonPool() which does use multiple threads and thus executes on multiple CPUs in this example. launch只是创建新的协程,而CommonPool将协程分派到ForkJoinPool.commonPool() ,它确实使用多个线程,因此在本例中在多个 CPU 上执行。

The code that follows launch invocation in {...} is called a suspending lambda . {...} launch调用之后的代码称为挂起 lambda What is it and how are suspending lambdas and functions implemented (compiled) as well as standard library functions and classes like startCoroutines , suspendCoroutine and CoroutineContext is explained in the corresponding Kotlin coroutines design document .它是什么以及如何实现(编译)挂起 lambda 和函数以及标准库函数和类,如startCoroutinessuspendCoroutineCoroutineContext ,在相应的Kotlin 协程设计文档中进行了解释。

Since I used coroutines only on JVM, I will talk about JVM backend, there are also Kotlin Native and Kotlin JavaScript but these backends for Kotlin are out of my scope.因为我只在 JVM 上使用协程,所以我将讨论 JVM 后端,还有 Kotlin Native 和 Kotlin JavaScript,但这些 Kotlin 后端超出了我的范围。

So let's start with comparing Kotlin coroutines to other languages coroutines.所以让我们从比较 Kotlin 协程和其他语言的协程开始。 Basically, you should know that there are two types of Coroutines: stackless and stackful.基本上,你应该知道有两种类型的 Coroutines:stackless 和 stackful。 Kotlin implements stackless coroutines - it means that coroutine doesn't have its own stack, and that limiting a little bit what coroutine can do. Kotlin 实现了无堆栈协程——这意味着协程没有自己的堆栈,这限制了协程可以做什么。 You can read a good explanation here .你可以在这里阅读一个很好的解释。

Examples:例子:

  • Stackless: C#, Scala, Kotlin无堆栈:C#、Scala、Kotlin
  • Stackful: Quasar, Javaflow Stackful:类星体、Javaflow

What it means that coroutine is like light-weight thread?协程就像轻量级线程是什么意思?

It means that coroutine in Kotlin doesn't have its own stack, it doesn't map on a native thread, it doesn't require context switching on a processor.这意味着 Kotlin 中的协程没有自己的堆栈,它不会映射到本地线程,也不需要处理器上的上下文切换。

What is the difference?有什么不同?

Thread - preemptively multitasking.线程 - 抢先式多任务处理。 ( usually ). 通常)。 Coroutine - cooperatively multitasking.协程 - 协同多任务处理。

Thread - managed by OS (usually).线程 - 由操作系统管理(通常)。 Coroutine - managed by a user.协程 - 由用户管理。

Are kotlin's coroutines actually running in parallel / concurrently? kotlin 的协程实际上是并行/并发运行的吗?

It depends, you can run each coroutine in its own thread, or you can run all coroutines in one thread or some fixed thread pool.这取决于,您可以在自己的线程中运行每个协程,也可以在一个线程或某个固定线程池中运行所有协程。

More about how coroutines execute here .有关协程如何执行的更多信息,请参见此处

Even in a multi-core system, there is only one coroutine running at any given time (is it right?)即使在多核系统中,在任何给定时间也只有一个协程在运行(对吗?)

No, see the previous answer.不,请参阅上一个答案。

Here I'm starting 100000 coroutines, what happens behind this code?这里我启动了 100000 个协程,这段代码背后发生了什么?

Actually, it depends.实际上,这取决于。 But assume that you write the following code:但是假设您编写了以下代码:

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

This code executes instantly.此代码立即执行。

Because we need to wait for results from async call.因为我们需要等待async调用的结果。

So let's fix this:所以让我们解决这个问题:

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

        job.join()
    }
}

When you run this program kotlin will create 2 * 100000 instances of Continuation , which will take a few dozen Mb of RAM, and in console, you will see numbers from 1 to 100000.当您运行此程序时,kotlin 将创建 2 * 100000 个Continuation实例,这将占用几十 Mb 的 RAM,并且在控制台中,您将看到从 1 到 100000 的数字。

So lets rewrite this code in this way:所以让我们以这种方式重写这段代码:

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

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

    job.join()
}

What we achieve now?我们现在实现了什么? Now we create only 100001 instances of Continuation , and this is much better.现在我们只创建了 100001 个Continuation实例,这要好得多。

Each created Continuation will be dispatched and executed on CommonPool (which is a static instance of ForkJoinPool).每个创建的 Continuation 将在 CommonPool(它是 ForkJoinPool 的静态实例)上分派和执行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM