Kotlin协程如何在内部工作? [英] How do Kotlin coroutines work internally?

查看:104
本文介绍了Kotlin协程如何在内部工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Kotlin如何在内部实现协程?

How does Kotlin implement coroutines internally?

协程被称为线程的较轻版本",我了解它们在内部使用线程来执行协程.

Coroutines are said to be a "lighter version" of threads, and I understand that they use threads internally to execute coroutines.

使用任何构建器功能启动协程时会发生什么?

What happens when I start a coroutine using any of the builder functions?

这是我对运行此代码的理解:

This is my understanding of running this code:

GlobalScope.launch {       <---- (A)
    val y = loadData()     <---- (B)  // suspend fun loadData() 
    println(y)             <---- (C)
    delay(1000)            <---- (D)
    println("completed")   <---- (E)
}

  1. 科特林开头有一个预定义的ThreadPool.
  2. (A),Kotlin在下一个可用的空闲线程(说Thread01)中开始执行协程.
  3. (B),Kotlin停止执行当前线程,并在下一个可用空闲线程(Thread02)中启动挂起功能loadData().
  4. 执行后返回(B)时,Kotlin在下一个可用的空闲线程中继续协程 (Thread03).
  5. (C)Thread03上执行.
  6. (D)Thread03停止.
  7. 1000毫秒后,在下一个空闲线程(例如Thread01)上执行(E).
  1. Kotlin has a pre-defined ThreadPool at the beginning.
  2. At (A), Kotlin starts executing the coroutine in the next available free thread (Say Thread01).
  3. At (B), Kotlin stops executing the current thread, and starts the suspending function loadData() in the next available free thread (Thread02).
  4. When (B) returns after execution, Kotlin continues the coroutine in the next available free thread (Thread03).
  5. (C) executes on Thread03.
  6. At (D), the Thread03 is stopped.
  7. After 1000ms, (E) is executed on the next free thread, say Thread01.

我理解正确吗?还是协程以不同的方式实现?

Am I understanding this correctly? Or are coroutines implemented in a different way?

推荐答案

协程与您描述的任何调度策略完全不同.协程基本上是suspend fun的调用链.暂停完全在您的控制之下:您只需致电suspendCoroutine.您将获得一个回调对象,因此可以调用其resume方法并返回到挂起的位置.

Coroutines are a completely separate thing from any scheduling policy that you describe. A coroutine is basically a call chain of suspend funs. Suspension is totally under your control: you just have to call suspendCoroutine. You'll get a callback object so you can call its resume method and get back to where you suspended.

在一些代码中,您可以看到暂停是一种非常直接和透明的机制,完全在您的控制之下:

Here's some code where you can see that suspension is a very direct and trasparent mechanism, fully under your control:

import kotlin.coroutines.*
import kotlinx.coroutines.*

var continuation: Continuation<String>? = null

fun main(args: Array<String>) {
    val job = GlobalScope.launch(Dispatchers.Unconfined) {
        while (true) {
            println(suspendHere())
        }
    }
    continuation!!.resume("Resumed first time")
    continuation!!.resume("Resumed second time")
}

suspend fun suspendHere() = suspendCancellableCoroutine<String> {
    continuation = it
}

以上所有代码在同一主线程上执行.根本没有多线程.

All the code above executes on the same, main thread. There is no multithreading at all going on.

launch的协程每次调用suspendHere()时都会自行挂起.它将延续回调写到continuation属性,然后您显式使用该延续来恢复协程.

The coroutine you launch suspends itself each time it calls suspendHere(). It writes the continuation callback to the continuation property, and then you explicitly use that continuation to resume the coroutine.

代码使用Unconfined协程分派器,该分派器根本不分派线程,它只是在您调用continuation.resume()的位置运行协程代码.

The code uses the Unconfined coroutine dispatcher which does no dispatching to threads at all, it just runs the coroutine code right there where you invoke continuation.resume().

考虑到这一点,让我们重新查看您的图表:

With that in mind, let's revisit your diagram:

GlobalScope.launch {       <---- (A)
    val y = loadData()     <---- (B)  // suspend fun loadData() 
    println(y)             <---- (C)
    delay(1000)            <---- (D)
    println("completed")   <---- (E)
}

  1. 科特林开头有一个预定义的ThreadPool.
  1. Kotlin has a pre-defined ThreadPool at the beginning.

它可能有也可能没有线程池. UI调度程序使用单个线程.

It may or may not have a thread pool. A UI dispatcher works with a single thread.

使线程成为协程分派器的目标的先决条件是存在与之关联的并发队列,并且该线程运行一个顶级循环,该循环从此队列中获取Runnable对象并执行它们.协程分派器只需将继续操作放在该队列中即可.

The prerequisite for a thread to be the target of a coroutine dispatcher is that there is a concurrent queue associated with it and the thread runs a top-level loop that takes Runnable objects from this queue and executes them. A coroutine dispatcher simply puts the continuation on that queue.

  1. (A),Kotlin在下一个可用的空闲线程(说Thread01)中开始执行协程.
  1. At (A), Kotlin starts executing the coroutine in the next available free thread (Say Thread01).

它也可以与您调用launch的线程相同.

It can also be the same thread where you called launch.

  1. (B),Kotlin停止执行当前线程,并在下一个可用空闲线程(Thread02)中启动挂起功能loadData().
  1. At (B), Kotlin stops executing the current thread, and starts the suspending function loadData() in the next available free thread (Thread02).

Kotlin无需停止任何线程即可挂起协程.实际上,协程的主要目的是不要启动或停止线程.线程的顶级循环将继续,并选择另一个可运行的线程.

Kotlin has no need to stop any threads in order to suspend a coroutine. In fact, the main point of coroutines is that threads don't get started or stopped. The thread's top-level loop will go on and pick another runnable to run.

此外,仅称您为suspend fun的事实并不重要.协程仅在显式调用suspendCoroutine时才会挂起自身.该函数也可以简单地返回而不会暂停.

Furthermore, the mere fact that you're calling a suspend fun has no significance. The coroutine will only suspend itself when it explicitly calls suspendCoroutine. The function may also simply return without suspension.

但让我们假设它确实调用了suspendCoroutine.在这种情况下,协程不再在任何线程上运行 .它已被挂起,直到某些代码在某处调用continuation.resume()后才能继续.该代码可以在将来的任何时间在任何线程上运行.

But let's assume it did call suspendCoroutine. In that case the coroutine is no longer running on any thread. It is suspended and can't continue until some code, somewhere, calls continuation.resume(). That code could be running on any thread, any time in the future.

  1. 执行后返回(B)时,Kotlin在下一个可用的空闲线程中继续协程 (Thread03).
  1. When (B) returns after execution, Kotlin continues the coroutine in the next available free thread (Thread03).

B不会执行后返回",协程仍在其内部时恢复.返回之前,它可能会暂停并恢复任意次数.

B doesn't "return after execution", the coroutine resumes while still inside its body. It may suspend and resume any number of times before returning.

  1. (C)Thread03上执行.
  2. (D)Thread03停止.
  3. 1000毫秒后,在下一个空闲线程(例如Thread01)上执行(E).
  1. (C) executes on Thread03.
  2. At (D), the Thread03 is stopped.
  3. After 1000ms, (E) is executed on the next free thread, say Thread01.

同样,没有线程被停止.协程被挂起,通常针对调度程序的机制用于调度1000 ms之后的恢复.届时,它将被添加到与调度程序关联的运行队列中.

Again, no threads are being stopped. The coroutine gets suspended and a mechanism, usually specific to the dispatcher, is used to schedule its resumption after 1000 ms. At that point it will be added to the run queue associated with the dispatcher.

为具体起见,让我们来看一些示例,这些示例说明了分发协程所需的代码类型.

For specificity, let's see some examples of what kind of code it takes to dispatch a coroutine.

Swing UI调度程序:

Swing UI dispatcher:

EventQueue.invokeLater { continuation.resume(value) }

Android UI调度程序:

Android UI dispatcher:

mainHandler.post { continuation.resume(value) }

ExecutorService调度程序:

ExecutorService dispatcher:

executor.submit { continuation.resume(value) } 

这篇关于Kotlin协程如何在内部工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆