理解 Kotlin 中的协程

4

我正在尝试理解 Kotlin 协程的工作原理以及为什么它们被认为比线程更高效。

到目前为止,我的理解是:

  1. 当一个 suspend 函数暂停时,在底层它自愿地将控制权交还给调用者,调用者也会同样做,直到我们达到管理所有内容的某种“根”调用,例如协程调度器。
  2. 调度器检查哪些已暂停的函数准备好继续执行,并告诉它们继续。
  3. 基本上它类似于一个带有线程池的队列。

这个大致正确吗?

我关心的是:

如果在一个挂起函数中调用了阻塞 I/O 的经典 Java API,简单地将其包装在协程中并不能自动使代码更高效,因为它不会与调度器合作,对于 CPU 密集型任务也是如此。

也就是说,它并不像 Node 事件循环那样,默认情况下所有 I/O 都是异步的,并且被转移到类似于 epoll 的机制,而只是底层的一个大线程池吗?


1
请参考此处链接,了解如何将阻塞代码包装在协程中。 - Sweeper
1个回答

2
  1. 它将控制权交还给线程(不确定您所说的“调用者”确切指的是什么)。为了支持这一点,所有参与运行协程的线程都有循环,以便可以向它们提交有限的工作片段。

  2. 对于delay()来说是正确的,但我认为对于大多数挂起函数来说情况正好相反。它们通过传递给它们的Continuation通知调度器它们已准备好恢复执行。

  3. 是的,它类似于线程池。在这种情况下,这些池被称为“Dispatchers”。

当您将异步或阻塞的Java代码包装在挂起函数或withContext中以便在协程中使用时,并不会提供任何效率上的好处。它只是使得在协程中使用它成为可能。其好处在于结构化并发和同步(顺序、易于阅读和理解)的代码。

在使用withContext包装的阻塞函数的情况下,它将在上下文中放置的调度器的线程上运行阻塞代码。
在使用suspendCancellableCoroutine包装的基于异步回调的调用的情况下,异步工作将在API使用的相同线程上完成,无论是否使用协程。通常情况下,这是由API提供的某个线程池。
可能官方关于协程的文档过分强调了它们相对于直接使用线程的性能优势,这导致了困惑,因为即使没有协程,我们通常也不会直接使用线程来处理这些任务。我们会使用API、第三方调度器(如Rx)或者java.concurrent

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接