Kotlin中线程与协程的区别
Kotlin中是否有任何特定的语言实现与其他语言的协同程序有所不同?
- 这意味着协程就像轻量级的线程?
- 有什么不同?
- kotlin的协同程序是否并行/同时运行?
- 即使在多核系统中,在任何给定的时间只有一个协程运行(是不是?)
这里我开始了100000个协程,这个代码背后发生了什么?
for(i in 0..100000){ async(CommonPool){ //run long running operations } }
由于我只在JVM上使用协程,所以我会讨论JVM后端,还有Kotlin Native和Kotlin JavaScript,但后端的Kotlin超出了我的范围。
所以让我们开始比较Kotlin协同程序和其他语言的协同程序。 基本上你应该知道这是两种types的协程:堆栈和堆栈。 Kotlin实现了无堆栈协程 – 这意味着协程没有自己的协议栈,并且限制了协程可以做的一些事情。 你可以在这里阅读一个很好的解释。
例子:
- Stackless:C#,Scala,Kotlin
- Stackful:类星体,Javaflow
这意味着协程就像轻量级的线程?
这意味着Kotlin中的协程没有自己的堆栈,它不会映射到本地线程上,也不需要在处理器上进行上下文切换。
有什么不同?
线程 – 抢先多任务。 ( 通常 )。 协程 – 协同多任务。
线程 – 由操作系统管理(通常)。 协程 – 由用户管理。
kotlin的协同程序是否并行/同时运行?
这取决于您可以在自己的线程中运行每个协程,也可以在一个线程或某个固定线程池中运行所有协程。
更多关于协程怎样在这里执行。
即使在多核系统中,在任何给定的时间只有一个协程运行(是不是?)
不,请参阅以前的答案。
这里我开始了100000个协程,这个代码背后发生了什么?
其实这取决于。 但是,假设你写下面的代码:
fun main(args: Array) { for (i in 0..100000) { async(CommonPool) { delay(1000) } } }
这段代码立即执行。
因为我们需要等待来自async
调用的结果。
所以我们来解决这个问题:
fun main(args: Array) = 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) = runBlocking { val job = async(CommonPool) { for (i in 0..100000) { delay(1) println(i) } } job.join() }
我们现在实现了什么? 现在我们只创建了100001个Continuation
实例,这样好多了。
每个创建的Continuation都将在CommonPool(这是ForkJoinPool的静态实例)上分派和执行。
这意味着协程就像轻量级的线程?
协程和线程一样,代表与其他协程(线程)同时执行的一系列动作。
有什么不同?
线程直接链接到相应操作系统(操作系统)中的本地线程,并占用大量资源。 特别是,它为堆栈消耗了大量内存。 这就是为什么你不能只创建10万个线程。 你可能会用完内存。 线程之间的切换涉及OS内核调度程序,就消耗的CPU周期而言,这是相当昂贵的操作。
另一方面,协程是纯粹的用户级语言抽象。 它并不绑定任何本地资源,在最简单的情况下,在JVM堆中只使用一个相对较小的对象。 这就是为什么创建10万条协程很容易。 在协程之间切换根本不涉及OS内核。 它可以像调用常规函数一样便宜。
kotlin的协同程序是否并行/同时运行? 即使在多核系统中,在任何给定的时间只有一个协程运行(是不是?)
协程可以运行或暂停。 挂起的协程与任何特定的线程都没有关联,但运行的协程在某个线程上运行(使用线程是在OS进程内执行任何操作的唯一方法)。 不同的协同程序是否都运行在同一个线程上(因此可能只使用多核系统中的一个CPU)或不同的线程(因此可能使用多个CPU)完全掌握在使用协程的程序员手中。
在Kotlin中,通过协程来控制协程的调度。 您可以在kotlinx.coroutines指南中阅读更多
这里我开始了100000个协程,这个代码背后发生了什么?
假设您正在使用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 。 它是什么,暂停lambdas和函数实现(编译)以及标准库函数和类,如startCoroutines
, suspendCoroutine
和CoroutineContext
在相应的Kotlin协同设计文档中解释 。