何时使用 Kotlin 协程来优化代码性能

And*_*rew 2 android kotlin kotlin-coroutines

目前,我正在尝试 kotlin 协程,我问自己一个问题:使用协程时是否有显着的性能提升?让我们看一些(理论)例子:

例一

val myUnsortedList = listOf(1, 2, 5, 6, 3, 10, 100)
fun doStuffWithSorted(list: List<Int>) { ... }

// Without coroutine
val sortedList = myUnsortedList.sorted()
doStuffWithSorted(sortedList)

coroutineScope {
   launch {
       val sortedList = myUnsortedList.sorted()
       doStuffWithSorted(sortedList)
   }
}
Run Code Online (Sandbox Code Playgroud)

示例二

fun doSomeHeavyOperations() { // doing heavy Operations but non blocking }

fun main() { doSomeHeavyOperations() }

//////////////// VERSUS //////////////////////

suspend fun doSomeHeavyOperations() { // doing heavy Operations but non blocking }

suspend fun main() {
    coroutineScope {
       launch {
         doSomeHeavyOperations()
       }
    }
}
Run Code Online (Sandbox Code Playgroud)

还有更多示例,也许你们中的一个可以提供一些,建议使用协程。所以我的最后一个问题是(包括上面的问题):什么时候应该考虑用协程来优化代码性能,什么时候代价大于获得的性能?

mig*_*WOZ 5

协程主要是一种涉及大量等待的计算工具,想想同步执行的网络调用。在这些情况下,调用线程除了等待服务器的响应之外什么也不做。

为了消除这个等待问题,我们使用异步编程,又称回调。因此,您启动一​​个网络调用并指定一个回调,一旦结果准备好就会调用该回调。但回调模型有它自己的问题,即回调地狱,如下面的代码所示。

fun someAPICalls(){
    getToken(){ token ->
        login(token) { userID -> 
             getUser(userID){ user -> 
                 // Save user in DB
                 // This nesting can be even deeper
             }
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这段代码不被认为是可管理的。借助 kotlin 的挂起函数,所有这些都简化为

fun someAPICalls() = viewModelScope.launch{
    val token  = getToken()            // suspending function calls (Retrofit, Room)
    val userId = login(token)
    val user   = getUser(userId)
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这与顺序代码的编写方式非常接近。

尽管还有其他选项(RX等)可用于解决回调问题,但它们确实引入了您需要学习的自己的语义。另一方面,编写协程代码与顺序代码没有太大不同,您只需要学习一些基本构造(调度程序、构建器等),这使得协程成为这种情况下的最佳选择

除此之外,还有一些其他场景可以有效地使用协程,其中一个用例就是 Android 等 UI 框架中使用的线程卸载实践。当您想要执行长时间运行的 CPU 密集型操作时,您不会在 UI 线程上执行此操作,而是将该操作卸载到后台线程。这也可以使用协程构建器之一非常干净地完成,例如lifecycleScope.launch(Dispatchers.Default) {}

哪些地方应该避免使用协程?

协程是线程之上的抽象,它需要线程来执行,就像线程需要CPU核心来执行一样。因此,协程管理会带来一定的开销,因此,如果您需要执行长时间运行的 CPU 密集型操作,并且可能需要使用多个线程,那么最好使用线程(Java ExecutorService 等)。