Поскольку я использовал сопрограммы только на JVM, я расскажу о бэкэнде JVM, есть также Kotlin Native и Kotlin JavaScript, но эти бэкенды для Kotlin выходят за рамки моей компетенции.
Итак, давайте начнем со сравнения сопрограмм Kotlin с сопрограммами других языков. По сути, вы должны знать, что есть два типа сопрограмм: без стека и со стеками. Kotlin реализует сопрограммы без стека - это означает, что сопрограмма не имеет собственного стека, и это немного ограничивает возможности сопрограммы. Вы можете прочитать хорошее объяснение здесь.
Примеры:
- Без стека: C #, Scala, Kotlin
- Полный: Quasar, Javaflow
Что означает, что сопрограмма похожа на легкий поток?
Это означает, что у сопрограммы в Kotlin нет собственного стека, она не отображается на нативный поток и не требует переключения контекста на процессоре.
В чем разница?
Thread - приоритетная многозадачность. (обычно). Coroutine - совместная многозадачность.
Тема - управляется ОС (обычно). Coroutine - управляется пользователем.
Действительно ли сопрограммы котлина работают параллельно / одновременно?
Это зависит от того, вы можете запускать каждую сопрограмму в ее собственном потоке, или вы можете запускать все сопрограммы в одном потоке или в каком-то фиксированном пуле потоков.
Подробнее о выполнении сопрограмм здесь.
Даже в многоядерной системе в любой момент времени работает только одна сопрограмма (правильно?)
Нет, см. Предыдущий ответ.
Здесь я запускаю 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).
person
Ruslan
schedule
25.03.2017