Kotlin 中是否有任何特定的语言实现,它与协程的其他语言实现不同?
- 协程就像轻量级线程是什么意思?
- 有什么区别?
- kotlin 协程实际上是并行/并发运行的吗?
- 即使在多核系统中,任何时候也只有一个协程在运行(对吗?)
这里我启动 100000 个协程,这段代码背后发生了什么?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
Kotlin 中是否有任何特定的语言实现,它与协程的其他语言实现不同?
这里我启动 100000 个协程,这段代码背后发生了什么?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
协程就像轻量级线程是什么意思?
协程和线程一样,表示与其他协程(线程)同时执行的一系列动作。
有什么区别?
线程直接链接到相应OS(操作系统)中的本机线程,并消耗大量资源。特别是,它为其堆栈消耗了大量内存。这就是为什么你不能只创建 100k 线程。您很可能内存不足。线程之间的切换涉及操作系统内核调度程序,就消耗的 CPU 周期而言,这是一项非常昂贵的操作。
另一方面,协程是纯粹的用户级语言抽象。它不绑定任何本机资源,并且在最简单的情况下,仅使用 JVM 堆中一个相对较小的对象。这就是为什么创建 100k 协程很容易的原因。协程之间的切换根本不涉及操作系统内核。它可以像调用常规函数一样便宜。
kotlin 的协程实际上是并行/并发运行的吗?即使在多核系统中,任何时候也只有一个协程在运行(对吗?)
协程可以运行或挂起。挂起的协程与任何特定线程无关,但正在运行的协程在某个线程上运行(使用线程是在 OS 进程内执行任何操作的唯一方法)。不同的协程是运行在同一个线程上(因此在多核系统中可能只使用单个 CPU)还是运行在不同的线程中(因此可能使用多个 CPU)完全取决于使用协程的程序员。
在 Kotlin 中,协程的调度是通过协程上下文控制的。您可以在 kotlinx.coroutines 指南中阅读更多相关信息
这里我启动 100000 个协程,这段代码背后发生了什么?
假设您正在使用项目中的launch
函数和CommonPool
上下文kotlinx.coroutines
(它是开源的),您可以在此处检查它们的源代码:
launch
在这里定义https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.ktCommonPool
在这里定义https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.ktlaunch
只是创建新的协程,同时CommonPool
将协程分派给ForkJoinPool.commonPool()
使用多个线程并因此在此示例中在多个 CPU 上执行的 a。
launch
调用之后的代码{...}
称为暂停 lambda。它是什么以及如何实现(编译)暂停 lambda 和函数以及标准库函数和类startCoroutines
,suspendCoroutine
并CoroutineContext
在相应的Kotlin 协程设计文档中进行了解释。
由于我只在 JVM 上使用协程,所以我将讨论 JVM 后端,还有 Kotlin Native 和 Kotlin JavaScript,但 Kotlin 的这些后端超出了我的范围。
因此,让我们先将 Kotlin 协程与其他语言的协程进行比较。基本上,你应该知道协程有两种类型:stackless 和 stackful。Kotlin 实现了无堆栈协程——这意味着协程没有自己的堆栈,这限制了协程的功能。你可以在这里阅读一个很好的解释。
例子:
协程就像轻量级线程是什么意思?
这意味着 Kotlin 中的协程没有自己的堆栈,它不映射到本机线程,它不需要处理器上的上下文切换。
有什么区别?
线程 - 抢先式多任务处理。(通常)。协程 - 协同多任务处理。
线程 - 由操作系统管理(通常)。协程 - 由用户管理。
kotlin 的协程实际上是并行/并发运行的吗?
这取决于,您可以在自己的线程中运行每个协程,也可以在一个线程或某个固定线程池中运行所有协程。
更多关于协程如何执行的信息。
即使在多核系统中,任何时候也只有一个协程在运行(对吗?)
不,请参阅上一个答案。
这里我启动 100000 个协程,这段代码背后发生了什么?
实际上,这取决于。但假设您编写以下代码:
fun main(args: Array<String>) {
for (i in 0..100000) {
async(CommonPool) {
delay(1000)
}
}
}
此代码立即执行。
因为我们需要等待async
调用的结果。
所以让我们解决这个问题:
fun main(args: Array<String>) = runBlocking {
for (i in 0..100000) {
val job = async(CommonPool) {
delay(1)
println(i)
}
job.join()
}
}
当你运行这个程序时,kotlin 将创建 2 * 100000 个实例Continuation
,这将占用几十 Mb 的 RAM,在控制台中,你会看到从 1 到 100000 的数字。
所以让我们用这种方式重写这段代码:
fun main(args: Array<String>) = runBlocking {
val job = async(CommonPool) {
for (i in 0..100000) {
delay(1)
println(i)
}
}
job.join()
}
我们现在取得了什么成就?现在我们只创建了 100001 个实例Continuation
,这要好得多。
每个创建的 Continuation 都将在 CommonPool(这是 ForkJoinPool 的静态实例)上调度和执行。