← Назад к вопросам

Какие знаешь компоненты в Coroutines?

2.0 Middle🔥 241 комментариев
#Kotlin основы#Архитектура и паттерны#Многопоточность и асинхронность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Компоненты Kotlin Coroutines

Coroutines представляют собой набор компонентов, которые вместе образуют целостную систему асинхронного программирования. Вот основные компоненты, которые я разделю на несколько категорий.

Базовые строительные блоки

CoroutineScope

Контекст, в котором запускаются корутины. Управляет временем жизни корутин через Job. Основные реализации:

  • GlobalScope - глобальная область видимости (используется с осторожностью)
  • CoroutineScope() - создание пользовательской области
  • Области жизненного цикла в Android: viewModelScope, lifecycleScope
class MyViewModel : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            // Корутина, автоматически отменяемая при очистке ViewModel
            val data = repository.loadData()
        }
    }
}

Job

Представляет отменяемую задачу с иерархической структурой. Управляет состоянием корутины:

  • New, Active, Completing, Completed, Cancelling, Cancelled
  • Можно отменять, ждать завершения, проверять состояние
val job = CoroutineScope(Dispatchers.IO).launch {
    // Длительная операция
}

// Позже отменяем
job.cancel()

Deferred

Job с результатом (аналог Future/Promise). Возвращается async-корутиной:

val deferred: Deferred<Int> = async {
    calculateSomething()
}

// Получаем результат (может быть приостановкой)
val result = deferred.await()

Контексты и диспетчеры

CoroutineContext

Набор элементов, определяющих поведение корутины:

  • Диспетчер - определяет поток выполнения
  • Job - управление жизненным циклом
  • Обработчик исключений - обработка ошибок
  • Имя - для отладки
val customContext = Dispatchers.IO + CoroutineName("MyCoroutine") + SupervisorJob()

CoroutineDispatcher

Определяет, на каком потоке/пуле потоков будет выполняться корутина:

  • Dispatchers.Default - для CPU-интенсивных задач (размер пула = количеству ядер)
  • Dispatchers.IO - для блокирующих IO-операций (динамический пул до 64 потоков)
  • Dispatchers.Main - главный поток (UI поток в Android/Swing/JavaFX)
  • Dispatchers.Unconfined - не привязан к конкретному потоку (стартует в текущем, продолжает в любом)
  • Пользовательские диспетчеры через newSingleThreadContext, newFixedThreadPoolContext

Строители корутин

launch

Запускает корутину без возвращаемого результата ("fire-and-forget"):

scope.launch {
    // Асинхронный код
    doWork()
    // Нет возвращаемого значения
}

async

Запускает корутину, возвращающую Deferred<T>:

val result1: Deferred<Int> = async { fetchData1() }
val result2: Deferred<Int> = async { fetchData2() }

// Параллельное выполнение с ожиданием обоих результатов
val sum = result1.await() + result2.await()

runBlocking

Блокирует текущий поток до завершения корутины (используется в основном в тестах и main-функциях):

fun main() = runBlocking {
    // Блокирует main-поток до завершения
    launch {
        delay(1000)
        println("World")
    }
    println("Hello")
}

withContext

Смена контекста внутри корутины с возвратом результата:

suspend fun loadData(): Data = withContext(Dispatchers.IO) {
    // Выполняется в IO-диспетчере
    performNetworkRequest()
}

Обработка исключений

CoroutineExceptionHandler

Обработчик необработанных исключений:

val handler = CoroutineExceptionHandler { _, exception ->
    println("Поймано исключение: $exception")
}

scope.launch(handler) {
    throw RuntimeException("Ошибка в корутине")
}

SupervisorJob и SupervisorScope

Режим, при котором сбой одной дочерней корутины не отменяет другие:

// SupervisorJob не распространяет отмену на родителя
val supervisor = SupervisorJob()

// Или используем supervisorScope
supervisorScope {
    launch {
        // Если эта корутина упадет, другие продолжат работу
        throw RuntimeException()
    }
    launch {
        // Эта корутина продолжит работу
        delay(1000)
    }
}

Потоковые API

Flow

Асинхронный поток данных (cold stream), аналогичный RxJava Observable:

fun getNumbers(): Flow<Int> = flow {
    for (i in 1..10) {
        delay(100)
        emit(i) // Выдача значения
    }
}

// Сбор
scope.launch {
    getNumbers()
        .filter { it % 2 == 0 }
        .map { it * 2 }
        .collect { value ->
            println(value)
        }
}

Channel

Горячий поток для коммуникации между корутинами (аналог BlockingQueue):

val channel = Channel<Int>()

// Producer
launch {
    for (x in 1..5) {
        channel.send(x * x)
    }
    channel.close()
}

// Consumer
launch {
    for (y in channel) {
        println(y)
    }
}

Специальные конструкции

select

Ожидание нескольких приостанавливаемых функций с выбором первой завершившейся:

suspend fun selectExample() {
    val channel1 = Channel<Int>()
    val channel2 = Channel<Int>()
    
    select<String> {
        channel1.onReceive { value ->
            "from channel1: $value"
        }
        channel2.onReceive { value ->
            "from channel2: $value"
        }
    }
}

Mutex и Semaphore

Средства синхронизации для корутин:

val mutex = Mutex()

suspend fun safeUpdate() {
    mutex.withLock {
        // Критическая секция
        sharedResource.update()
    }
}

Интеграционные компоненты

Для интеграции с существующими API существуют:

  • suspendCancellableCoroutine - адаптация callback-API к suspend-функциям
  • callbackFlow - создание Flow из callback-API
  • CompletableDeferred - ручное создание Deferred
suspend fun awaitCallback(): Result = suspendCancellableCoroutine { continuation ->
    val callback = object : SomeCallback {
        override fun onSuccess(result: Result) {
            continuation.resume(result)
        }
        override fun onError(error: Throwable) {
            continuation.resumeWithException(error)
        }
    }
    api.registerCallback(callback)
    
    // Обработка отмены
    continuation.invokeOnCancellation {
        api.unregisterCallback(callback)
    }
}

Эти компоненты вместе образуют мощную, но гибкую систему, которая позволяет писать асинхронный код в последовательном стиле, управлять ресурсами, обрабатывать ошибки и эффективно использовать потоки выполнения.