在每一篇关于kotlin的文章中,JetBrain都谈到了异步编程。但是我不明白为什么它们被称为异步?正如我所理解的kotlin协程——这是一个带有pre-init线程池的状态机。我们有一个worker线程池和一个io线程池。对我来说,这只是一个多线程编程。如果我们向协程发送阻塞代码,一个线程将被阻塞。如果我们使用async方法(来自默认协程库),它给我们一种异步工作的错觉,但这只不过是将'job'发送到另一个线程。
另一个问题,如果我们使用异步io协程。但这IOAPI异步,不是kotlin协程。Java没有好的io异步api与其他语言相比(可能是错误的)…NET我知道已经重建了他们的异步api(作为IOCP)使用C#任务。NET有专门的线程池等待所有的iOS应用程序,所以一个线程可以处理许多IO操作。但是kotlin协程并没有集成到javanio中,当我们从协程调用nio时(无论有没有Dispatcher.IO),我们只是要求一个线程等待来自nio的数据。JavaNIO有自己的线程池用于etos或iocp,所以使用kotlin协程,当我们要求Dispatcher.IO给我们一个线程来等待来自NIO的结果时,我们会产生开销,之后NIO实现会创建自己的线程(池)来等待来自socket的数据。我们现在有两个线程(池)来等待,而不是一个线程(池)。
所以协程只是允许我们以一种简单的方式将作业发送到另一个线程。如果您的api没有使用kotlin协程以异步方式实现,您就不能同时使用一个线程执行多项操作。
我会尝试从不同的角度来看待这个问题。协程是基于挂起函数的。挂起函数是异步的。
协程是状态机的事实在这里无关紧要。状态机可以转换为异步函数链,反之亦然。
与其将Dispatcher视为“线程池”,不如将它们视为Retor模式或事件循环。是的,您可以阻止事件循环。但是,这并不意味着事件循环不是异步的,也不意味着您应该这样做。
你所谓的“异步作业的错觉”只有在内部不使用挂起函数时才是“错觉”。由于静态编程语言代码的很大一部分是挂起的(flow、Ktor等),实际上,你的大部分代码将是有效的异步的。
Java的NIO与静态编程语言回调有不同的抽象。可以使用将它们转换为协程
最后一部分大部分是错误的:
协程只是允许我们以简单的方式将作业发送到另一个线程。如果您的api没有使用kotlin协程以异步方式实现,您就不能同时使用一个线程执行多项操作。
单个Dispatcher线程将在多个协程之间进行上下文切换,除非其中一个协程阻塞:不使用任何挂起函数并执行IO或CPU密集型任务。
他们谈论异步编程,因为协程主要(但不限于)作为库出售,以使异步编程更容易(主观)。但是正如你正确地指出的,协程本身没有异步。如果你在协程中执行阻塞代码,它将阻塞底层线程。
但是要理解的一点是,协程只有在与挂起函数耦合时才真正有优势,其中线程除了等待结果(回调)什么都不做。所以你可以使用同一个线程来做十个这样的调用,而不是等待。另一个主要优点是使用协程编写的异步代码更容易编写和维护。例如,以下是使用回调的异步调用
fun callAPI(){
getToken{ token ->
auth(token){ authResult ->
doSomething(authResult){ finalResult ->
// use final result
}
}
}
}
这可以简化为使用协程和挂起函数跟随
fun callAPI() = scope.launch(){
val token = getToken()
val authResult = auth(token)
val finalResult = doSomething(authResult)
}
现在您可以使用协程来启动多个长时间运行的阻塞任务,但您不会看到任何优势。因为在这种情况下,协程只不过是线程之上的无用抽象。