为什么 kotlin 协程被认为是轻量级的?

Joe*_*Dow 6 multithreading kotlin kotlin-coroutines

如果协程仍然使用线程并行运行代码,为什么它被认为是轻量级的?

在我的理解 kotlin 的suspend功能被编译器转换成状态机,其中每个分支可以在开发人员定义的相同或不同线程上运行。协程构建器,例如,launch{}负责它并CoroutineContext定义要运行的线程。

通过将代码块发送到利用相同线程的线程池来实现并行性

有一个关于 100k 协程和 100k 线程的基准测试,其中协程毫无问题地通过并且线程抛出异常(可能是 OutOfMemory)。这让我想到我在这里遗漏了一些东西。

你能帮我理解这里遗漏了什么,是什么让协程并行运行代码块 100k 而不像线程那样超过内存限制?

Ani*_*ahu 8

文章中指点

每个Thread都有自己的堆栈,通常为 1MB。64k 是 JVM 中每个线程允许的最小堆栈空间量,而 Kotlin 中的简单协程仅占用几十字节的堆内存。

Coroutine Dispatcher 有一个限制,即只能创建一定数量的线程。

例如Dispatchers.IO限制为 64 个线程,Dispatchers.Default限制处理器上的核心数(2、4、6、8 等)Dispatchers.Unconfined无法创建新线程,它在其他人先前创建的线程上运行调度员,这里有证据:500 次操作有 10 毫秒的睡眠时间大约需要 5 秒(单线程,因为它不能产生一个新线程)自己尝试一下

协程粘在一个线程上,一旦到达暂停点,它就会离开线程并释放它,让它在等待时接收另一个协程。这种方式使用更少的线程和更少的内存,可以完成大量的并发工作。

协程由类似回调的对象管理挂起和恢复,该对象Continuation作为最后一个参数添加到suspend编译时用关键字标记的函数中,该函数像其他对象一样存在于堆中并负责协程的恢复,所以RAM 中不需要数千 MB 的空间来保持所有线程处于活动状态。一个典型的 60-70 个线程在 max using 下创建CommonPool并被重用(如果创建了新的协程,它会等待另一个完成)。


Mar*_*nik 8

主要的节省来自这样一个事实:单个线程可以通过协作多任务处理来运行任意数量的协程。当您启动 100,000 个协程时,它们会在与 CPU 核心数量一样多的线程上运行,但是当您启动 100,000 个线程时,JVM 会创建同样多的本机线程。请注意,两种情况下的并行级别相同,并且受限于 CPU 核心的数量。

唯一改变的是调度:在经典情况下,操作系统挂起和恢复线程,将它们分配给 CPU 核心。对于协程,协程会暂停自身(这是它们的合作方面),并Dispatcher稍后恢复它们,同时运行其他协程。