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

Где запустится бесконечный цикл внутри корутины?

3.0 Senior🔥 101 комментариев
#Kotlin основы#Многопоточность и асинхронность

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

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

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

Бесконечный цикл внутри корутины: контекст выполнения

Бесконечный цикл внутри корутины будет выполняться в том контексте диспетчера, который был назначен корутине при её запуске или наследован от родительской корутины. Ключевой момент: корутина не блокирует поток, на котором запущена, если внутри цикла используются suspend-функции или явно вызывается yield(). В противном случае может произойти блокировка потока.

Основные сценарии выполнения

1. Бесконечный цикл с suspend-функциями

Если внутри цикла вызываются suspend-функции (например, delay(), withContext(), сетевые вызовы), корутина будет приостанавливаться, позволяя потоку выполнять другие задачи. Цикл выполняется в контексте диспетчера, но не блокирует поток.

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch(Dispatchers.Default) {
        while (true) {
            println("Цикл работает в ${Thread.currentThread().name}")
            delay(1000) // Suspend-функция - корутина приостанавливается
        }
    }
    delay(5000)
}

В этом примере цикл выполняется в фоновом потоке из Dispatchers.Default, но благодаря delay() поток не блокируется и может использоваться для других корутин.

2. Бесконечный цикл БЕЗ suspend-функций (опасный случай)

Если цикл не содержит suspend-вызовов, он будет блокировать поток диспетчера, что может привести к проблемам производительности.

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch(Dispatchers.Default) {
        while (true) {
            // Тяжёлые вычисления без suspend-функций
            // Этот поток будет полностью заблокирован!
        }
    }
}

Для таких случаев следует использовать:

  • Dispatchers.Default для CPU-интенсивных операций
  • yield() для периодической приостановки
  • Переход в withContext(Dispatchers.Default) для вычислений

3. Использование yield() для кооперативной многозадачности

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        while (true) {
            // Выполнение работы
            yield() // Явная приостановка для других корутин
        }
    }
}

Критические аспекты

  1. Отмена корутины: Бесконечный цикл должен быть отменяемым. Используйте isActive или ensureActive():
while (isActive) {
    // Работа
    delay(100)
}
  1. Выбор диспетчера:

    • Dispatchers.Main - UI-поток (Android)
    • Dispatchers.IO - для операций ввода-вывода
    • Dispatchers.Default - для вычислений
    • Dispatchers.Unconfined - запускается в текущем потоке
  2. Structured Concurrency: Всегда запускайте корутины в CoroutineScope с жизненным циклом, чтобы избежать утечек:

viewModelScope.launch {
    while (isActive) {
        // Работа
        delay(1000)
    }
}

Практические рекомендации

  • Всегда добавляйте suspend-вызовы в бесконечные циклы внутри корутин
  • Используйте isActive для проверки отмены корутины
  • Избегайте блокирующих операций в циклах без proper диспетчера
  • Для CPU-интенсивных циклов используйте Dispatchers.Default с периодическим yield()

Бесконечный цикл в корутине — это нормальный паттерн для фоновых задач, опросов, слушателей событий, но его реализация должна быть кооперативной и отменяемой, чтобы не нарушать работу всей системы корутин.