Kotlin中线程和协程的区别
Difference between thread and coroutine in Kotlin
Kotlin 中是否有任何特定的语言实现,与其他语言的协程实现不同?
- 什么叫协程像轻量级线程?
- 有什么区别?
- kotlin 协程实际上 运行 并行/并发吗?
- 即使在多核系统中,在任何给定时间也只有一个协程运行(对吗?)
这里我启动了 100000 个协程,这段代码背后发生了什么?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
由于我只在 JVM 上使用协程,所以我将讨论 JVM 后端,还有 Kotlin Native 和 Kotlin JavaScript 但是 Kotlin 的这些后端不在我的范围内。
那么让我们开始比较 Kotlin 协程和其他语言的协程。基本上,您应该知道有两种类型的协程:stackless 和 stackful。 Kotlin 实现了无栈协程——这意味着协程没有自己的栈,这在一定程度上限制了协程的功能。你可以阅读一个很好的解释here。
示例:
- Stackless:C#、Scala、Kotlin
- Stackful:Quasar、Javaflow
What it means that coroutine is like light-weight thread?
这意味着 Kotlin 中的协程没有自己的堆栈,不映射到本机线程,不需要处理器上的上下文切换。
What is the difference?
线程 - 抢占式多任务处理。 (usually)。
Coroutine - 协作式多任务处理。
线程 - 由 OS 管理(通常)。
协程 - 由用户管理。
Are kotlin's coroutines actually running in parallel / concurrently?
视情况而定,您可以 运行 每个协程在其自己的线程中,或者您可以 运行 所有协程在一个线程或某个固定线程池中。
更多关于协程如何执行的信息here。
Even in a multi-core system, there is only one coroutine running at any given time (is it right?)
没有,看之前的回答
Here I'm starting 100000 coroutines, what happens behind this code?
实际上,这取决于。但是假设你写了下面的代码:
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()
}
我们现在取得了什么成就?现在我们只创建了 Continuation
的 100001 个实例,这样就好多了。
每个创建的 Continuation 都将在 CommonPool(它是 ForkJoinPool 的静态实例)上分派和执行。
What means that coroutine is like light-weight thread?
协程,就像线程一样,代表一系列与其他协程(线程)并发执行的动作。
What is the difference?
一个线程直接链接到相应OS(操作系统)中的native线程,消耗相当大的资源。特别是,它的堆栈会消耗大量内存。这就是为什么您不能只创建 100k 个线程的原因。您很可能 运行 内存不足。在线程之间切换涉及 OS 内核调度程序,就消耗的 CPU 周期而言,这是一项相当昂贵的操作。
另一方面,协程纯粹是用户级语言抽象。它不绑定任何本机资源,在最简单的情况下,它只使用 JVM 堆中的一个相对较小的对象。这就是为什么很容易创建 100k 协程的原因。协程之间的切换根本不涉及 OS 内核。它可以像调用常规函数一样便宜。
Are kotlin's coroutines actually running in parallely / concurrently?
Even in multi-core system, there is only one coroutine running at any given time (is it right?)
协程可以 运行ning 或挂起。挂起的协程不与任何特定线程关联,而是某个线程上的 运行ning 协程 运行(使用线程是在 OS 进程内执行任何操作的唯一方法)。不同的协程是否全部 运行 在同一个线程上(因此在多核系统中可能只使用一个 CPU )或在不同的线程中(因此可能使用多个 CPU )纯粹是在使用协程的程序员手中。
在 Kotlin 中,协程的调度是通过协程上下文控制的。您可以在
Guide to kotlinx.coroutines
Here I'm starting 100000 coroutines, what happens behind this code?
假设您正在使用来自 kotlinx.coroutines
项目(开源)的 launch
函数和 CommonPool
上下文,您可以在此处查看它们的源代码:
launch
定义在这里 https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Builders.kt
CommonPool
定义在这里https://github.com/Kotlin/kotlinx.coroutines/blob/master/core/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/CommonPool.kt
launch
只是创建新协程,而 CommonPool
将协程分派给 ForkJoinPool.commonPool()
,它确实使用多个线程,因此在本例中在多个 CPU 上执行.
{...}
中 launch
调用之后的代码称为 暂停 lambda。它是什么以及如何暂停 lambda 和函数实现(编译)以及标准库函数和 类 如 startCoroutines
、suspendCoroutine
和 CoroutineContext
在相应的 Kotlin coroutines design document.
Kotlin 中是否有任何特定的语言实现,与其他语言的协程实现不同?
- 什么叫协程像轻量级线程?
- 有什么区别?
- kotlin 协程实际上 运行 并行/并发吗?
- 即使在多核系统中,在任何给定时间也只有一个协程运行(对吗?)
这里我启动了 100000 个协程,这段代码背后发生了什么?
for(i in 0..100000){
async(CommonPool){
//run long running operations
}
}
由于我只在 JVM 上使用协程,所以我将讨论 JVM 后端,还有 Kotlin Native 和 Kotlin JavaScript 但是 Kotlin 的这些后端不在我的范围内。
那么让我们开始比较 Kotlin 协程和其他语言的协程。基本上,您应该知道有两种类型的协程:stackless 和 stackful。 Kotlin 实现了无栈协程——这意味着协程没有自己的栈,这在一定程度上限制了协程的功能。你可以阅读一个很好的解释here。
示例:
- Stackless:C#、Scala、Kotlin
- Stackful:Quasar、Javaflow
What it means that coroutine is like light-weight thread?
这意味着 Kotlin 中的协程没有自己的堆栈,不映射到本机线程,不需要处理器上的上下文切换。
What is the difference?
线程 - 抢占式多任务处理。 (usually)。 Coroutine - 协作式多任务处理。
线程 - 由 OS 管理(通常)。 协程 - 由用户管理。
Are kotlin's coroutines actually running in parallel / concurrently?
视情况而定,您可以 运行 每个协程在其自己的线程中,或者您可以 运行 所有协程在一个线程或某个固定线程池中。
更多关于协程如何执行的信息here。
Even in a multi-core system, there is only one coroutine running at any given time (is it right?)
没有,看之前的回答
Here I'm starting 100000 coroutines, what happens behind this code?
实际上,这取决于。但是假设你写了下面的代码:
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()
}
我们现在取得了什么成就?现在我们只创建了 Continuation
的 100001 个实例,这样就好多了。
每个创建的 Continuation 都将在 CommonPool(它是 ForkJoinPool 的静态实例)上分派和执行。
What means that coroutine is like light-weight thread?
协程,就像线程一样,代表一系列与其他协程(线程)并发执行的动作。
What is the difference?
一个线程直接链接到相应OS(操作系统)中的native线程,消耗相当大的资源。特别是,它的堆栈会消耗大量内存。这就是为什么您不能只创建 100k 个线程的原因。您很可能 运行 内存不足。在线程之间切换涉及 OS 内核调度程序,就消耗的 CPU 周期而言,这是一项相当昂贵的操作。
另一方面,协程纯粹是用户级语言抽象。它不绑定任何本机资源,在最简单的情况下,它只使用 JVM 堆中的一个相对较小的对象。这就是为什么很容易创建 100k 协程的原因。协程之间的切换根本不涉及 OS 内核。它可以像调用常规函数一样便宜。
Are kotlin's coroutines actually running in parallely / concurrently? Even in multi-core system, there is only one coroutine running at any given time (is it right?)
协程可以 运行ning 或挂起。挂起的协程不与任何特定线程关联,而是某个线程上的 运行ning 协程 运行(使用线程是在 OS 进程内执行任何操作的唯一方法)。不同的协程是否全部 运行 在同一个线程上(因此在多核系统中可能只使用一个 CPU )或在不同的线程中(因此可能使用多个 CPU )纯粹是在使用协程的程序员手中。
在 Kotlin 中,协程的调度是通过协程上下文控制的。您可以在 Guide to kotlinx.coroutines
Here I'm starting 100000 coroutines, what happens behind this code?
假设您正在使用来自 kotlinx.coroutines
项目(开源)的 launch
函数和 CommonPool
上下文,您可以在此处查看它们的源代码:
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.kt
launch
只是创建新协程,而 CommonPool
将协程分派给 ForkJoinPool.commonPool()
,它确实使用多个线程,因此在本例中在多个 CPU 上执行.
{...}
中 launch
调用之后的代码称为 暂停 lambda。它是什么以及如何暂停 lambda 和函数实现(编译)以及标准库函数和 类 如 startCoroutines
、suspendCoroutine
和 CoroutineContext
在相应的 Kotlin coroutines design document.