Kotlin 协程是如何调度的

How Kotlin coroutines are scheduled

最近我阅读了很多关于 Kotlin 协程的文章和视频,尽管我很努力,但我仍然无法理解它们。

我想我终于找到了一种方法来说明我的问题:

class MyViewModel() : CoroutineScope {

override val coroutineContext = Dispatchers.Main + Job()

    fun foo() = launch(handler) {
        Log.e("test", "A")
    }
}

class MainActivity : Activity() {
    override fun onCreate() {
        MainViewModel().foo()
        Log.e("test", "B")
    }
}

这个输出是:

E/test: B
E/test: A

而且我不明白这是怎么回事,我只使用了一个线程(主线程)。如果我的代码按顺序执行,当我到达行 log(B)... log(A) 时应该已经打印出来了。

协程库是否在内部使用其他线程来完成此操作?这是我能想到的唯一解释,但在文档中没有找到任何这样的解释。

PS:抱歉将 android 混入其中,但此代码:

fun main() {
    GlobalScope.launch(Dispatchers.Unconfined) { // launch new coroutine in background and continue
        print(Thread.currentThread().name + "World!") // print after delay
    }
    (0 .. 1000).forEach { print(".") }
}

似乎按预期工作并打印: main @coroutine#1World!...........................

因为1 thread == sequential work

希望我的问题有道理,感谢阅读!

在幕后,Main 调度程序使用 Handler 来 post MessageQueue 的 Runnable。基本上,它会被添加到事件队列的末尾。这意味着它将很快执行,但不会立即执行。因此,为什么“B”在“A”之前打印。

您可以在 this article 中找到更多信息。

OP 编辑​​(在阅读本文之前阅读上面的文章):

只是想澄清为什么上面的 android 示例运行良好,以防有人仍然疑惑。

fun main() {
    GlobalScope.launch(Dispatchers.Unconfined) { // launch new coroutine in background and continue
        print(Thread.currentThread().name + "World!") // print after delay
    }
    (0 .. 1000).forEach { print(".") }
}

我们将 GlobalScope 设置为使用 UNCONFINED 调度程序,并且此调度程序已将 isDispatchNeeded 设置为 falsefalse 表示 "schedule in the current thread",这就是我们看到日志按顺序打印的原因。 UNCONFINED 不应在常规代码中使用。

所有其他调度程序 isDispatchNeeded 设置为 true 甚至 UI 调度程序。参见:https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/is-dispatch-needed.html

(顺便说一句,如果我们不指定,GlobalScope 使用 Default 调度程序)