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

When to use Kotlin Coroutines to optimize code performance

目前,我正在对 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)
   }
}

例子二

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()
       }
    }
}

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

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

为了消除这个等待问题,我们使用异步编程,也就是回调。所以你开始一个网络调用并指定一个回调,一旦结果准备好就会调用它。但是回调模型有其自身的问题,the callback hell,如下面的代码所示。

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

如您所见,这段代码不是可管理的。使用 kotlin 的暂停功能,所有这些都减少到

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

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

尽管还有其他选项(RX etc) available to solve the callbacks issue, they do introduce their own semantics 你需要学习。另一方面,编写协程代码与其顺序代码没有什么不同,你只需要学习一些基本结构(Dispatchers , Builders 等),这使得协程成为这种情况下的最佳选择

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

哪些地方应该避免协程?

协程是线程之上的抽象,它需要一个线程来执行,就像线程需要一个 CPU 核心来执行一样。因此,协程管理引入了一定的开销,因此,如果您需要执行可能需要使用多个线程的长 运行 CPU 绑定操作,您最好使用线程(Java ExecutorService 等)。