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
设置为 false
。
false
表示 "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
调度程序)
最近我阅读了很多关于 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
设置为 false
。
false
表示 "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
调度程序)