首页 > 解决方案 > 协程的执行顺序是什么?

问题描述

考虑 kotlin 中的以下代码。

val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
   println("inside coroutine")
}
println("outside coroutine")

我们在 Main(UI) 线程中创建了一个协程,协程之后有一些代码。
我知道在实际代码中这样做没有多大意义,但这只是一个理论问题。

考虑到协程在主线程中运行,为什么println("outside coroutine")总是先执行?
我本以为有时我会先在 coroutine 之外看到,而在其他时候,首先在 coroutine 内部,有点像两个线程。
谁(OS 或 Coroutines 实现)决定先运行协程之外的 coe?

标签: androidkotlincoroutinekotlin-coroutines

解决方案


考虑到协程在主线程中运行,为什么 println("outside coroutine") 总是先执行?

让我们想象一下,您的代码是这样的:

someView.post {
   println("inside post")
}
println("outside post")

在这里,我们创建一个Runnable(lambda 表达式) 并将其传递给post()some Viewpost()说这Runnablerun()在主应用程序线程上......最终。这Runnable被放在Looper主应用程序线程使用的工作队列中,当它Runnable到达队列的顶部时它会被执行(或多或少 - 细节是更混乱的 IIRC,但在这里并不重要)。

但是如果您在主应用程序线程上执行此代码,println("outside post")将始终首先打印。被Runnable放置到队列中以便稍后执行,但您仍在主应用程序线程上执行,因此即使队列为空,Runnable在您将主应用程序线程的控制权返回给 Android 之前,它也不会运行。因此,在调用 之后post(),继续执行println("outside post")

在幕后,Dispatchers.Main基本上是在使用post()(同样,细节更复杂,但对于这个讨论来说不是太重要)。因此,当您launch()使用协程时,该 lambda 表达式会排队等待最终在主应用程序上执行。但是,您已经在主应用程序线程上,因此执行正常继续,并且println("outside post")在协程有机会做任何事情之前被打印出来。

假设您的代码改为:

val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
   println("inside coroutine")
}
scope.launch {
   println("inside another coroutine")
}

现在您处于理论上可以先打印这些行中的任何一行的情况。您正在排队两个 lambda 表达式,由调度程序决定在什么时候在哪个线程上运行什么。在实践中,如果总是首先打印“内部协程”,我不会感到惊讶,因为一个简单的实现Dispatchers.Main将在没有其他约束的情况下使用 FIFO 排序(例如,协程在 I/O 上被阻塞)。但是,您不应该假设这两个协程的特定调用顺序。


推荐阅读