Оскільки я використовував підпрограми лише на JVM, я буду говорити про бекенд JVM, є також Kotlin Native та Kotlin JavaScript, але ці серверні програми для Kotlin виходять за межі мого обсягу.
Отже, почнемо з порівняння котлінських програм Котліна та інших мов. По суті, ви повинні знати, що існує два типи корутинів: безстаровий і штабельний. Kotlin реалізує безпроблемні програми - це означає, що програма не має власного стеку, і це трохи обмежує можливості, які може зробити програма. Ви можете прочитати хороше пояснення тут .
Приклади:
- Бездоганні: C #, Scala, Kotlin
- Складені: Quasar, Javaflow
Що це означає, що програма є схожою на легку нитку?
Це означає, що програма в Kotlin не має власного стека, вона не відображається у власному потоці, не вимагає перемикання контексту на процесорі.
Яка різниця?
Потік - попереджувально багатозадачність. ( зазвичай ). Coroutine - спільна багатозадачність.
Потік - управляється ОС (зазвичай). Coroutine - керується користувачем.
Чи насправді програми 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
, що займе кілька десятків Мб оперативної пам'яті, а в консолі ви побачите цифри від 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
, і це набагато краще.
Кожне створене Продовження буде відправлене та виконане на CommonPool (який є статичним екземпляром ForkJoinPool).