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
}
}
Answers:
由于我仅在JVM上使用协程,因此我将讨论JVM后端,也有Kotlin Native和Kotlin JavaScript,但这些Kotlin后端不在我的讨论范围之内。
因此,让我们开始将Kotlin协程与其他语言的协程进行比较。基本上,您应该知道协程有两种类型:无堆栈和堆栈。Kotlin实现了无堆栈的协程-这意味着协程没有自己的堆栈,这限制了协程的功能。您可以在这里阅读很好的解释。
例子:
协程就像轻量级线程一样意味着什么?
这意味着Kotlin中的协程没有自己的堆栈,它不映射在本机线程上,不需要在处理器上进行上下文切换。
有什么不同?
线程-抢先多任务。(通常)。协程-协作多任务。
线程-通常由OS管理。协程-由用户管理。
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的静态实例)上调度并执行。
throws SuspendExecution
和Kotlinsuspend
修改器之间的一对一对应关系。当然,实现细节完全不同,但是用户体验却非常相似。
什么是协程像轻质螺纹?
协程就像线程一样,表示与其他协程(线程)同时执行的一系列动作。
有什么不同?
线程直接链接到相应OS(操作系统)中的本机线程,并且消耗大量资源。特别是,它为堆栈消耗了大量内存。这就是为什么您不能仅创建100k线程。您可能会用完内存。线程之间的切换涉及OS内核调度程序,就消耗的CPU周期而言,这是一项非常昂贵的操作。
另一方面,协程纯粹是用户级别的语言抽象。它不绑定任何本机资源,在最简单的情况下,它仅在JVM堆中使用一个相对较小的对象。这就是为什么创建100k协程很容易的原因。在协程之间切换根本不涉及OS内核。它可以像调用常规函数一样便宜。
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.kt在launch
刚刚创造了新的协同程序,而CommonPool
调度协程到ForkJoinPool.commonPool()
其不使用多线程,因此在本例中多个CPU执行。
launch
调用后的代码{...}
称为暂挂lambda。它是什么以及如何实现(编译(编译))的lambda和函数以及标准库函数和类之类的startCoroutines
,suspendCoroutine
并CoroutineContext
在相应的Kotlin协程设计文档中进行了说明。
Executor
,这两者之间有任何关系吗?