【发布时间】:2021-11-12 06:49:25
【问题描述】:
Jetbrains 在每篇关于 kotlin 的文章中都谈到了异步编程。但我不明白为什么它们被称为异步? 据我了解 kotlin 协程 - 这是一个具有预初始化线程池的状态机。我们有一个工作线程池和一个 io 线程池。对我来说,这只是一个多线程编程。如果我们向协程发送阻塞代码,线程将被阻塞。如果我们使用异步方法(来自默认协程库),它会给我们一种异步工作的错觉,但这只不过是将“作业”发送到另一个线程。
另一个问题,如果我们将异步 io 与协程一起使用。但这是 IO API 异步,而不是 kotlin 协程。与其他语言相比,Java 没有好的 io async api(可能是错误的)。据我所知,.NET 已经重建了他们的异步 api(作为 IOCP)以使用 C# 任务,并且.NET 有专用的线程池来等待应用程序的所有 io,因此一个线程可以处理许多 IO 操作。但是 kotlin 协程没有集成到 java nio 中,当我们从协程(有或没有 Dispatcher.IO)调用 nio 时,我们只是要求一个线程等待来自 nio 的数据。 Java NIO 有自己的用于 epoll 或 iocp 的线程池,因此使用 kotlin 协程时,当要求 Dispatcher.IO 给我们一个线程来等待 NIO 的结果时,我们会产生开销,然后 NIO 实现会创建自己的线程(池)来等待数据从插座。我们现在有两个等待,而不是一个线程(池)。
所以协程只是让我们以一种简单的方式将作业发送到另一个线程。如果您的 api 没有使用 kotlin 协程以异步方式实现,则您不能同时使用一个线程执行多项操作。
【问题讨论】:
-
你真的在这里提出了很多不同的问题——最好在 Kotlin 论坛上讨论这个问题。一般来说,协程让你在执行过程中等待而不阻塞任何线程。是的,如果您使用协程执行阻塞 IO,那么您需要阻塞一些 IO 线程。但是,如果您挂起而不是阻塞,那么不,您不会阻塞任何线程。关于命名,我不会将协程称为异步。事实上,主要目标之一是获得异步编程的性能,但没有异步回调地狱。挂起函数是同步的。
-
关于 NIO:从未尝试过,但用协程/挂起函数包装异步通道应该是可能的并且非常容易。这样我们可以例如执行 10 个并行 IO 操作,每个操作都将是传统的“阻塞”(实际上是挂起)
read()/write()函数,我们将仅使用单个线程(或更多,无论我们喜欢什么)来执行此操作。 NIO 可能会产生一些内部线程,但我想它会使用其中的一些,它不会创建 10 个线程。这样我们就有 2-4 个线程来处理所有 10 个并行连接。但是再一次 - 我自己没有尝试过。 -
对您的问题的简单回答是,他们没有像您期望的那样使用异步一词的狭义定义。
-
我不确定我是否理解正确。是的,如果您的代码主要阻塞 IO,那么在协程中运行它不会给您带来任何(?)性能优势。它不会神奇地使阻塞 IO 变为非阻塞——它只会产生线程。但是,如果您使用协程包装异步 IO,如果您使用为您执行此操作的库,或者您等待 IO 以外的其他东西,例如另一个任务,一些延迟等,那么您可以在不浪费线程的情况下执行此操作,并且仍然保持代码的同步性质。我认为这就是协程的主要内容。
-
是的,我认为我们在这里错过了一个体面的暂停 IO 库。它可能不会成为标准库的一部分,因为 Kotlin 的 stdlib 必须作为库包含在应用程序中,因此它应该保持苗条。 JetBrains 似乎在做这样的事情 (github.com/Kotlin/kotlinx-io),但它远未准备好。现在我们有其他很好的解决方案,比如
Ktorweb 框架,它可以完成我所描述的,但适用于 web 应用程序。我们也有一些带有挂起 API 的 3rd 方库,但挂起 API 本身并不能保证线程/资源的最佳使用。
标签: java multithreading kotlin asynchronous