异步编程正在获得动力,因为它允许更有效地利用处理器时间。因此,应用程序可以在每单位时间内处理更多请求,并且可以更好地扩展。
目前,实现异步方法有几个选项:
- 回调
- 反应式编程
- 协程
协程
协程允许您将应用程序代码保持其通常形式,执行在后台任务之间切换的所有操作。
suspend fun doSomething(arg: Argument) {
val firstValue = sendRequest(arg)
val secondValue = sendAnotherRequest(firstValue)
}
在示例中,我们用 suspend 关键字标记 doSomething 函数,这意味着它的答案不会立即出现。该函数本身还一个接一个地执行一些异步请求。
从常规函数调用挂起函数
不能从常规函数调用挂起函数,因为 JVM 不清楚应在哪个线程上执行该函数。
如果我们想在同一线程上执行一个函数,我们可以使用 runBlocking。当然,此选项将不允许我们有效地利用CPU资源。但它可以在测试或命令行界面中使用。
fun casualFunction() {
runBlocking { suspendFunction() }
}
fun suspend suspendFunction() {
doSomething()
}
另一种方法是使用单独的线程池。下面的示例使用 Dispatchers.IO,默认为 max(64, number of cores),但您可以设置自定义线程池。
fun casualFunction() {
CoroutineScope(Dispatchers.IO).launch {
delay(1000)
println("Launch finished")
}
println("Casual function finished")
}
重要的是要了解对启动块的调用将发生在不同的线程中,并且该方法将继续执行,因此“Casual function finished”将首先打印出来。
在挂起函数内运行阻塞操作
如果我们需要在挂起函数中运行繁重的函数(例如 IO 操作),该怎么办?如果我们只是运行它,它将阻塞当前线程,并且可能会破坏异步编程的整个想法。
相反,我们可以为慢速操作定义一个单独的上下文,以免阻塞现有线程。
suspend fun suspendFunction() {
val context = Executors.newFixedThreadPool(10).asCoroutineDispatcher()
withContext(context) {
runSomethingSlow()
}
}
现在,外部调度程序将挂起并将控件委托给我们的自定义上下文。完成后,自定义上下文会将控件返回到外部调度程序。