Как корутины устроены под капотом

Ответ

Корутины в Kotlin — это легковесные потоки, работающие поверх JVM. Они выполняются в рамках существующих потоков, используя механизм suspend-функций и continuation passing style (CPS).

Как это работает:

  1. Suspend-функции компилятор преобразует в конечный автомат с Continuation.
  2. При вызове suspend-функции корутина может быть приостановлена (suspendCoroutine), а позже возобновлена (resume).
  3. Dispatchers определяют, на каком потоке выполняется корутина (Main, IO, Default).

Пример:

suspend fun fetchData(): String {
    delay(1000)  // suspend-функция
    return "Data"
}

Под капотом:

  • delay приостанавливает корутину, сохраняя состояние в Continuation.
  • После завершения delay корутина возобновляется с сохранённым контекстом.

Корутины эффективнее потоков, т.к. не блокируют потоки и минимизируют накладные расходы.

Ответ 18+ 🔞

Давай разберём эту штуку, а то в документации написано так, что мозг вытекает. Короче, корутины в Kotlin — это не потоки, а такие... лёгкие виртуальные работяги, которые юзают уже готовые потоки JVM, не создавая новых. Представь, что у тебя есть одна бригада (поток), а корутины — это мужики в этой бригаде, которые умеют вовремя присесть отдохнуть (suspend), чтобы другой мог поработать, пока первый кофе пьёт. И так по кругу. Овердохуища эффективности!

Как эта магия работает, ёпта:

  1. Suspend-функции — это основа основ. Компилятор смотрит на них и думает: «Э, сабака сука, тут нужно сделать конечный автомат с какой-то Continuation». И делает. По сути, он разбивает твою функцию на куски-состояния.
  2. Когда ты вызываешь такую функцию, корутина может сказать: «Всё, я пас» (suspendCoroutine), сохранить все свои игрушки (состояние) и отойти. А потом, когда данные придут или таймер сработает, её пинают: «Эй, просыпайся, работай!» (resume).
  3. Dispatchers — это начальники, которые решают, на какой стройке (потоке) будет трудиться наш виртуальный работяга. На главной (Main), на складе (IO) или где попало (Default).

Вот тебе пример, чтобы не быть голословным:

suspend fun fetchData(): String {
    delay(1000)  // Магическая suspend-функция — типа "спи одну секунду"
    return "Data"
}

А теперь, что под капотом, бля:

  • Когда выполнение доходит до delay, корутина не тупо ждёт, блокируя поток. Нет! Она такая: «Ядрёна вошь, я сохраню все свои переменные в эту штуку Continuation и отключусь. Разбудите, когда секунда пройдёт».
  • Поток в этот момент — свободен! Он может другую корутину выполнять или ещё какую хернёй заниматься.
  • Секунда прошла — корутину будят, достают сохранённый контекст из Continuation и говорят: «На, всё как было, продолжай с return "Data"».

Вот и вся хитрость. Они эффективнее потоков, потому что не создают этих тяжеловесных сущностей ОС, не тратят время на переключение контекста, и один поток может обслуживать овердохуища таких виртуальных работяг. Не «бензина жрёт», а экономит ресурсы, как шведская девушка. Красота, да и только.