【问题标题】:What is the order of execution with coroutines?协程的执行顺序是什么?
【发布时间】:2020-02-14 22:17:18
【问题描述】:

考虑以下 kotlin 中的代码。

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

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

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

【问题讨论】:

  • Dispatchers.Main 在主循环器上运行。任何在其上launched 的协程都将排队等待执行,因此它将始终稍后运行,因为主循环器将首先执行其他“东西”(生命周期方法、ui 回调等)。

标签: android kotlin coroutine kotlin-coroutines


【解决方案1】:

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

假设您的代码是这样的:

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

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

但如果您在主应用程序线程上执行此代码,println("outside post") 将始终首先打印。 Runnable 被放入队列中稍后执行,但您仍在主应用程序线程上执行,因此即使队列为空,Runnable 也不会运行,直到您返回主应用程序线程的控制权回到安卓。因此,在调用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 上被阻塞)。但是,您不应该假设这两个协程的调用顺序是特定的。

【讨论】:

  • 为了完整起见,还有(现已弃用)选项CoroutineStart.UNDISPATCHED,这将导致启动的协程立即开始执行,并且只有在遇到暂停点时才将控制权交给父协程。这再次是确定性行为,但它表明协程的晚启动并不是根本性的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-20
相关资源
最近更新 更多