为什么不在主线程上执行所有 cpu 密集型操作 - 使用协程?
Why not do all cpu intensive operation on main thread - using co-routines?
我知道我们不应该这样做,并且很清楚为什么会这样。
想详细了解,求详细解释-
由于协程是非阻塞的,我相信 IO 或 CPU 密集型操作也可以在主线程上完成 - 即 - 在 Main Dispatcher 上启动协程,并且 android 不会抱怨(跳过 .. 帧或在主线程上进行大量工作),也不用担心 ANR。
我的问题是为什么我们更喜欢非 UI 调度程序来为 IO 和 CPU 密集型工作启动协程 -
到
- 使用线程池并实现并行? - 但协同例程不是并发模型,也没有实现并行性,或者它实现了并发,并且在 kotlin 中以不同的方式实现。
或者,是否有其他原因。我明白 - 在其他线程模型中,如 RxJava 等。它需要在不同的线程中执行,因为线程正在阻塞。
例如-
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
doSomeHeavyOp() // blocks the main thread for 8 sec, can give ANR
viewModel.fetchDataFromServer()
}
private fun doSomeHeavyOp(){ // blocks the main thread
Thread.sleep(8000) // sleep for 8 secs
}
}
但是,如果我使用协程
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch { // launches co-routine on main-thread
doSomeHeavyOp()
}
viewModel.fetchDataFromServer() // this will be executed immediately - as in sequentially
}
private suspend fun doSomeHeavyOp(){ // does-not block the main thread
delay(8000) // suspend for 8 secs
}
}
第二个示例不会阻塞 main/ui 线程,因此 UI 线程不会同时发生 ANR 和繁重的计算。
我想你误解了协程不阻塞是什么意思。他们不会让每个阻塞代码都神奇地变成非阻塞的。如果在协程中执行阻塞 I/O 操作,它仍然会阻塞线程。如果你在协程中执行 CPU 密集型计算,你仍然会占用一些线程,它无法做任何其他事情。
协同程序使我们有可能暂时停止执行代码以等待某些事情而不会阻塞线程。这称为挂起以区别于阻塞。在协程中你可以挂起,但如果你阻塞,你仍然会正常阻塞。
让我们看这个例子:
suspend fun runOnMainThread() {
// 1
withContext(Dispatchers.IO) {
// 2
}
// 3
}
我们从主线程执行这个函数。我们必须等待 IO 操作才能继续 3
,因此通常这意味着主线程在等待时被阻塞。在我们的例子中,这个函数的执行被挂起,主线程在等待 IO 的同时可以自由地做其他事情。如果没有协程和异步操作,这样的事情是不可能的。
在使用协同程序时,我们应该尽可能地尝试挂起而不是阻塞。如果我们使用提供 API 的 IO 库,它不会阻塞,但会暂停,那么我们就可以完全按照您所说的去做——在主调度程序中正常使用这个 IO 库。但是只要我们使用传统的阻塞操作,我们就应该切换到IO Dispatcher。
你说它不会阻塞 main/ui 线程是正确的。但它仍然会在主线程上 运行 。主线程已经忙于处理很多事情。最重要的一个是以某种刷新率(可能每秒 50 次)连续渲染 UI。
那为什么还要增加负载呢?当您有其他调度程序和核心时。
另外 android 如果您在主线程上进行此类工作,确实会抱怨跳帧。在 android logcat“应用程序可能在其主线程上做了太多工作”中搜索此内容。
我知道我们不应该这样做,并且很清楚为什么会这样。
想详细了解,求详细解释-
由于协程是非阻塞的,我相信 IO 或 CPU 密集型操作也可以在主线程上完成 - 即 - 在 Main Dispatcher 上启动协程,并且 android 不会抱怨(跳过 .. 帧或在主线程上进行大量工作),也不用担心 ANR。
我的问题是为什么我们更喜欢非 UI 调度程序来为 IO 和 CPU 密集型工作启动协程 -
到
- 使用线程池并实现并行? - 但协同例程不是并发模型,也没有实现并行性,或者它实现了并发,并且在 kotlin 中以不同的方式实现。
或者,是否有其他原因。我明白 - 在其他线程模型中,如 RxJava 等。它需要在不同的线程中执行,因为线程正在阻塞。
例如-
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
doSomeHeavyOp() // blocks the main thread for 8 sec, can give ANR
viewModel.fetchDataFromServer()
}
private fun doSomeHeavyOp(){ // blocks the main thread
Thread.sleep(8000) // sleep for 8 secs
}
}
但是,如果我使用协程
class HomeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
lifecycleScope.launch { // launches co-routine on main-thread
doSomeHeavyOp()
}
viewModel.fetchDataFromServer() // this will be executed immediately - as in sequentially
}
private suspend fun doSomeHeavyOp(){ // does-not block the main thread
delay(8000) // suspend for 8 secs
}
}
第二个示例不会阻塞 main/ui 线程,因此 UI 线程不会同时发生 ANR 和繁重的计算。
我想你误解了协程不阻塞是什么意思。他们不会让每个阻塞代码都神奇地变成非阻塞的。如果在协程中执行阻塞 I/O 操作,它仍然会阻塞线程。如果你在协程中执行 CPU 密集型计算,你仍然会占用一些线程,它无法做任何其他事情。
协同程序使我们有可能暂时停止执行代码以等待某些事情而不会阻塞线程。这称为挂起以区别于阻塞。在协程中你可以挂起,但如果你阻塞,你仍然会正常阻塞。
让我们看这个例子:
suspend fun runOnMainThread() {
// 1
withContext(Dispatchers.IO) {
// 2
}
// 3
}
我们从主线程执行这个函数。我们必须等待 IO 操作才能继续 3
,因此通常这意味着主线程在等待时被阻塞。在我们的例子中,这个函数的执行被挂起,主线程在等待 IO 的同时可以自由地做其他事情。如果没有协程和异步操作,这样的事情是不可能的。
在使用协同程序时,我们应该尽可能地尝试挂起而不是阻塞。如果我们使用提供 API 的 IO 库,它不会阻塞,但会暂停,那么我们就可以完全按照您所说的去做——在主调度程序中正常使用这个 IO 库。但是只要我们使用传统的阻塞操作,我们就应该切换到IO Dispatcher。
你说它不会阻塞 main/ui 线程是正确的。但它仍然会在主线程上 运行 。主线程已经忙于处理很多事情。最重要的一个是以某种刷新率(可能每秒 50 次)连续渲染 UI。
那为什么还要增加负载呢?当您有其他调度程序和核心时。
另外 android 如果您在主线程上进行此类工作,确实会抱怨跳帧。在 android logcat“应用程序可能在其主线程上做了太多工作”中搜索此内容。