Kotlin 协程如何设法在不阻塞主线程的情况下安排主线程上的所有协程?
How does Kotlin coroutines manage to schedule all coroutines on the main thread without block it?
我一直在 android 中试验 Kotlin 协程。我使用以下代码试图理解它的行为:
fun onStartButtonPressed(view: View) {
Log.d(TAG, "Outside Scope: ${Thread.currentThread().id}")
lifecycle.coroutineScope.launch {
Log.d(TAG, "Top Level Scope: ${Thread.currentThread().id}")
val t1 = launch {
Log.d(TAG, "Task 1 Scope: ${Thread.currentThread().id}")
for (i in 1..2) {
delay(1000L)
withContext(Dispatchers.Main) {
viewBinding.task1ProgressBar.progress = i * 50
}
}
}
val t2 = launch {
Log.d(TAG, "Task 2 Scope: ${Thread.currentThread().id}")
for (i in 1..4) {
delay(1000L)
withContext(Dispatchers.Main) {
viewBinding.task2ProgressBar.progress = i * 25
}
}
}
t1.join()
t2.join()
withContext(Dispatchers.Main) {
Log.d(TAG, "Completion Scope: ${Thread.currentThread().id}")
viewBinding.startButton.isEnabled = true
viewBinding.resetButton.isEnabled = true
viewBinding.statusTextView.text = "All tasks have been finished."
}
}
viewBinding.statusTextView.text = "All tasks have been started."
viewBinding.startButton.isEnabled = false
viewBinding.resetButton.isEnabled = false
}
代码的输出是:
2021-06-15 21:05:13.066 18079-18079/com.demo.coroutinedemo D/MainActivity: Outside Scope: 2
2021-06-15 21:05:13.132 18079-18079/com.demo.coroutinedemo D/MainActivity: Top Level Scope: 2
2021-06-15 21:05:13.140 18079-18079/com.demo.coroutinedemo D/MainActivity: Task 1 Scope: 2
2021-06-15 21:05:13.142 18079-18079/com.demo.coroutinedemo D/MainActivity: Task 2 Scope: 2
2021-06-15 21:05:17.189 18079-18079/com.demo.coroutinedemo D/MainActivity: Completion Scope: 2
这对我来说非常有帮助,因为 join()
方法会阻塞主线程,但 UI 没有冻结,这是为什么?
这正是协程被发明的原因以及它们与线程并发的区别。协程不会阻塞,但会暂停(好吧,它们可以两者兼顾)。而“暂停”不仅仅是“阻止”的别称。当它们挂起时(例如通过调用 join()
),它们有效地释放了运行它们的线程,因此它可以在其他地方做其他事情。是的,这听起来在技术上是不可能的,因为我们正在执行某个函数的代码,我们必须在那里等待,但是......欢迎使用协同程序:-)
您可以将其视为函数被切割成两部分:join()
之前和之后。第一部分安排后台操作并立即 returns。当后台操作完成时,它会在主线程上安排第二部分。这不是协同程序在内部的工作方式(函数并没有真正被削减,它们创建了延续),但如果您熟悉执行程序或事件循环,那么您可以很容易地想象它们的工作方式。
delay()
也是一个挂起函数,所以它释放线程 运行 它并在指定的持续时间后安排执行它下面的代码。
我一直在 android 中试验 Kotlin 协程。我使用以下代码试图理解它的行为:
fun onStartButtonPressed(view: View) {
Log.d(TAG, "Outside Scope: ${Thread.currentThread().id}")
lifecycle.coroutineScope.launch {
Log.d(TAG, "Top Level Scope: ${Thread.currentThread().id}")
val t1 = launch {
Log.d(TAG, "Task 1 Scope: ${Thread.currentThread().id}")
for (i in 1..2) {
delay(1000L)
withContext(Dispatchers.Main) {
viewBinding.task1ProgressBar.progress = i * 50
}
}
}
val t2 = launch {
Log.d(TAG, "Task 2 Scope: ${Thread.currentThread().id}")
for (i in 1..4) {
delay(1000L)
withContext(Dispatchers.Main) {
viewBinding.task2ProgressBar.progress = i * 25
}
}
}
t1.join()
t2.join()
withContext(Dispatchers.Main) {
Log.d(TAG, "Completion Scope: ${Thread.currentThread().id}")
viewBinding.startButton.isEnabled = true
viewBinding.resetButton.isEnabled = true
viewBinding.statusTextView.text = "All tasks have been finished."
}
}
viewBinding.statusTextView.text = "All tasks have been started."
viewBinding.startButton.isEnabled = false
viewBinding.resetButton.isEnabled = false
}
代码的输出是:
2021-06-15 21:05:13.066 18079-18079/com.demo.coroutinedemo D/MainActivity: Outside Scope: 2
2021-06-15 21:05:13.132 18079-18079/com.demo.coroutinedemo D/MainActivity: Top Level Scope: 2
2021-06-15 21:05:13.140 18079-18079/com.demo.coroutinedemo D/MainActivity: Task 1 Scope: 2
2021-06-15 21:05:13.142 18079-18079/com.demo.coroutinedemo D/MainActivity: Task 2 Scope: 2
2021-06-15 21:05:17.189 18079-18079/com.demo.coroutinedemo D/MainActivity: Completion Scope: 2
这对我来说非常有帮助,因为 join()
方法会阻塞主线程,但 UI 没有冻结,这是为什么?
这正是协程被发明的原因以及它们与线程并发的区别。协程不会阻塞,但会暂停(好吧,它们可以两者兼顾)。而“暂停”不仅仅是“阻止”的别称。当它们挂起时(例如通过调用 join()
),它们有效地释放了运行它们的线程,因此它可以在其他地方做其他事情。是的,这听起来在技术上是不可能的,因为我们正在执行某个函数的代码,我们必须在那里等待,但是......欢迎使用协同程序:-)
您可以将其视为函数被切割成两部分:join()
之前和之后。第一部分安排后台操作并立即 returns。当后台操作完成时,它会在主线程上安排第二部分。这不是协同程序在内部的工作方式(函数并没有真正被削减,它们创建了延续),但如果您熟悉执行程序或事件循环,那么您可以很容易地想象它们的工作方式。
delay()
也是一个挂起函数,所以它释放线程 运行 它并在指定的持续时间后安排执行它下面的代码。