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

Что такое Dispatchers в корутинах?

2.3 Middle🔥 171 комментариев
#Многопоточность и асинхронность

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

Dispatchers в корутинах

Dispatcher — это объект, который определяет, на каком потоке выполняется корутина. Это критически важный концепт в Kotlin coroutines, так как правильный выбор dispatcher влияет на производительность и отзывчивость приложения.

Основная роль Dispatcher

Корутины — это лёгкие потоки, и dispatcher указывает, какой реальный поток (из пула потоков) будет выполнять код корутины. Различные диспетчеры оптимизированы для разных типов работы:

Основные Dispatchers

1. Dispatchers.Main

Выполняет корутину на главном (UI) потоке. Используется для работы с UI элементами:

launchUI {
    val data = fetchData()  // фоновая работа
    textView.text = data    // обновление UI
}

// То же самое явно:
launch(Dispatchers.Main) {
    val data = fetchData()
    textView.text = data
}

Особенности:

  • Выполняется на Main потоке
  • Единственный способ обновить UI в Android
  • Блокирующий код здесь замерзит приложение
  • Default dispatcher в launch {} и GlobalScope.launch {}

2. Dispatchers.IO

Оптимизирован для блокирующих I/O операций (чтение файлов, сетевые запросы, БД):

launch(Dispatchers.IO) {
    val response = apiService.getUsers()  // сетевой запрос
    val users = response.users
    
    withContext(Dispatchers.Main) {
        updateUI(users)  // переключаемся на Main
    }
}

Особенности:

  • Использует пул потоков размером примерно 64 потока
  • Каждая блокирующая операция требует отдельный поток
  • Отлично подходит для одновременных I/O операций
  • Не подходит для CPU-интенсивных задач

3. Dispatchers.Default

Оптимизирован для CPU-интенсивной работы (вычисления, сортировка, обработка данных):

launch(Dispatchers.Default) {
    val result = heavyComputation()  // интенсивные вычисления
    val sorted = largeList.sortedBy { it.value }
    
    withContext(Dispatchers.Main) {
        showResult(result)
    }
}

Особенности:

  • Пул потоков размером = количеству ядер процессора
  • Минимизирует переключение контекста
  • Лучше не использовать блокирующие операции
  • Используется в withContext { } по умолчанию

4. Dispatchers.Unconfined

Выполняет на потоке, где была запущена корутина:

launch(Dispatchers.Unconfined) {
    println("Running on: ${Thread.currentThread().name}")
}

Особенности:

  • Редко используется в production
  • Удобно для тестирования
  • Может привести к неожиданному поведению
  • Не рекомендуется для обычного кода

Практический пример: правильный паттерн

private suspend fun fetchAndDisplayUsers() {
    // Работа с БД на IO потоке
    val users = withContext(Dispatchers.IO) {
        userRepository.getUsers()  // блокирующий запрос
    }
    
    // Обновление UI на Main потоке
    withContext(Dispatchers.Main) {
        usersAdapter.submitList(users)
        progressBar.visibility = View.GONE
    }
}

// Или в viewModelScope
viewModelScope.launch {
    try {
        val users = withContext(Dispatchers.IO) {
            userRepository.getUsers()
        }
        _uiState.value = UiState.Success(users)
    } catch (e: Exception) {
        _uiState.value = UiState.Error(e.message)
    }
}

ViewModelScope vs GlobalScope

Предпочитай viewModelScope:

// ❌ Плохо
GlobalScope.launch(Dispatchers.IO) {
    val data = repository.getData()
    // корутина может выполняться после уничтожения ViewModel!
}

// ✓ Хорошо
viewModelScope.launch(Dispatchers.IO) {
    val data = repository.getData()
    // отменяется при уничтожении ViewModel
}

Переключение между Dispatchers

launch(Dispatchers.Main) {
    // Код на Main потоке
    
    val data = withContext(Dispatchers.IO) {
        // Переключились на IO поток
        apiService.fetchData()
    }
    
    // Вернулись на Main поток автоматически
    updateUI(data)
}

Правило выбора Dispatcher

ОперацияDispatcherПочему
Обновление UIMainЕдинственный доступный
REST API, БДIOОптимизирован для блокирующих операций
ВычисленияDefaultИзбегает перегрузки пула IO
ТестированиеUnconfinedСинхронное выполнение

Custom Dispatcher (редко)

val customDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()

launch(customDispatcher) {
    // выполняется на выбранном потоке
}

Итого

Dispatcher — это сердце асинхронности в Kotlin. Правильный выбор dispatcher:

  • Улучшает производительность
  • Предотвращает замерзание UI
  • Обеспечивает безопасность потоков
  • Делает код предсказуемым и тестируемым

Основное правило: IO для блокирующих операций, Default для вычислений, Main для UI.