Введение в корутины

Обзорно знакомимся с корутинами — лёгким способом писать асинхронный код.

Корутина — лёгкая «приостанавливаемая» задача, которая позволяет писать асинхронный код в привычном последовательном стиле.

Когда программа ждёт ответа от сети или файла, можно либо заблокировать поток, либо организовать асинхронность. Корутины Kotlin делают второе просто и читаемо. Это обзорный урок: цель — понять идею, а не освоить всё.

suspend-функции

Функцию, которая умеет «приостанавливаться» и возобновляться позже, помечают словом suspend. Вызвать её можно только из другой корутины или suspend-функции.

import kotlinx.coroutines.*

suspend fun loadData(): String {
    delay(1000)            // неблокирующая пауза
    return "Данные загружены"
}

fun main() = runBlocking {
    println(loadData())
}

Вывод:

Данные загружены

launch — запуск корутины

Билдер launch запускает новую корутину, которая работает параллельно с остальным кодом, не блокируя поток.

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(500)
        println("Мир")
    }
    println("Привет")
}

Вывод:

Привет
Мир

Почему это «лёгкие» задачи

Главное преимущество — экономичность. Потоков операционной системы можно создать тысячи, и они тяжёлые. Корутин же можно запустить сотни тысяч: они не привязаны к отдельному потоку и во время паузы (например, delay) освобождают поток для другой работы.

Поток (Thread)Корутина
тяжёлый, дорогойлёгкая, дешёвая
блокируется при ожиданииприостанавливается, освобождая поток
их тысячиих сотни тысяч

Как работает под капотом

Ключевое отличие delay от обычного «сна» потока (Thread.sleep) в том, что delay не блокирует поток: корутина приостанавливается, поток в это время выполняет другие задачи, а по истечении времени корутина возобновляется. Технически компилятор превращает suspend-функцию в конечный автомат: каждая точка приостановки — это место, где функция может «замереть» и продолжиться позже. Запускать корутины нужно внутри области (scope), которую в примерах задаёт runBlocking.

Частые ошибки

  • Вызывать suspend-функцию из обычной. Её можно вызвать только из корутины или другой suspend-функции.
  • Использовать Thread.sleep вместо delay. sleep блокирует поток, теряя смысл корутин.
  • Путать корутины и потоки. Корутина — это не поток, а лёгкая задача, которая может выполняться в любом потоке.

Итог

  • Корутины позволяют писать асинхронный код в последовательном стиле.
  • suspend-функции умеют приостанавливаться и вызываются из корутин.
  • launch запускает корутину, не блокируя поток.
  • delay приостанавливает корутину без блокировки потока — в этом ключевое отличие от Thread.sleep.
Проверьте себя
1. Что означает модификатор suspend у функции?
AФункция выполняется мгновенно
BФункция может приостанавливаться и возобновляться, вызывается из корутины
CФункция приватная
DФункция статическая
2. Чем delay отличается от Thread.sleep?
AНичем
Bdelay не блокирует поток, а приостанавливает корутину, освобождая поток
Cdelay блокирует весь процесс
DThread.sleep быстрее
3. Почему корутин можно создать гораздо больше, чем потоков?
AКорутины не выполняют код
BКорутины лёгкие и не привязаны к отдельному потоку ОС
CПотоки запрещены в Kotlin
DКорутины используют GPU