Dispatchers — concepte cheie, tipuri de dispatchere și cum funcționează

Autor: IT Sectr Publicat: 2026-06-22 Timp de citire: 9 min

Dispatchers în Kotlin Coroutines — componente ale CoroutineContext care determină firele de execuție pentru corutine: Main (fir UI), IO (rețea și disc), Default (sarcini CPU-intensive) și Unconfined (firul curent). Fiecare dispatcher gestionează un pool specializat de fire, optimizat pentru un tip specific de muncă. Conform ghidului JetBrains, 2024, alegerea dispatcherului potrivit este critică pentru performanța și stabilitatea aplicației.

Principalele puncte

  • Dispatchers — dispatchere predefinite pentru corutine, care gestionează distribuirea sarcinilor pe fire
  • Dispatchers.Main — firul UI Android, pentru actualizarea interfeței și lucrul cu View
  • Dispatchers.IO — pool pentru cereri de rețea, citire/scriere fișiere și baze de date (până la 64 de fire)
  • Dispatchers.Default — pool pentru sarcini CPU-intensive în funcție de numărul de nuclee ale procesorului
  • Dispatchers.Unconfined — moștenirea firului curent, fără comutare

Ce sunt Dispatchers?

Dispatchers — sunt implementări ale interfeței CoroutineDispatcher, care sunt elemente ale CoroutineContext. Ele determină pe ce fir sau pool de fire va fi executată corutina. La crearea unei corutine prin launch sau async, dispatcherul poate fi transmis ca prim parametru: launch(Dispatchers.IO) { ... }. Dacă dispatcherul nu este specificat, se moștenește din CoroutineScope extern.

Kotlin oferă patru dispatchere încorporate: Main, IO, Default, Unconfined. Fiecare dispatcher folosește propriul pool de fire, optimizat pentru un tip specific de operații. Alegerea corectă a dispatcherului determină performanța aplicației: o alegere greșită duce la întârzieri ale interfeței, la subutilizarea nucleelor CPU sau la consumul ineficient al firelor.

DispatcherPool de fireMax. fireUtilizare
Dispatchers.MainUnul (UI)1Actualizare UI, LiveData, View
Dispatchers.IOPool IO64 (limitedParallelism)Rețea, fișiere, baze de date
Dispatchers.DefaultPool CPUN nucleeSortare, parsare, calcule
Dispatchers.UnconfinedFirul curentN/AOperații intermediare, teste

Dispatchers.Main: firul UI

Dispatchers.Main — dispatcherul care execută corutinele pe firul principal Android. Este destinat operațiilor legate de interfață: actualizare TextView, apelare notifyDataSetChanged, lucrul cu LiveData și StateFlow. În Android, acest dispatcher este implementat prin Handler (Looper.getMainLooper()).

kotlin
// Comutare corectă la Main pentru actualizări UI
viewModelScope.launch(Dispatchers.IO) {
    val data = repository.fetchData()
    withContext(Dispatchers.Main) {
        _uiState.value = data
    }
}

Dacă corutina este deja pe dispatcherul Main, un withContext(Dispatchers.Main) suplimentar nu creează costuri suplimentare — dispatcherul verifică firul curent și omite comutarea. withContext este modul preferat de a comuta între dispatchere.

Dispatchers.IO: rețea și operații pe disc

Dispatchers.IO — dispatcher optimizat pentru operații de intrare-ieșire: cereri HTTP (Ktor, OkHttp), citirea și scrierea fișierelor, lucrul cu Room sau SQLDelight. Folosește un pool de 64 de fire în mod implicit, scalabil sub sarcină. Fiecare nouă cerere IO poate crea un fir suplimentar până la atingerea limitei.

Limitarea paralelismului

Pentru a controla numărul de operații IO simultane, utilizați limitedParallelism(). Această funcție creează un nou dispatcher cu limitarea numărului de fire paralele, prevenind epuizarea pool-ului în operații masive.

kotlin
val limitedIo = Dispatchers.IO.limitedParallelism(4)

// Încarcă 100 de fișiere cu limita de 4 operații concurente
coroutineScope {
    val files = (1..100).map { index ->
        async(limitedIo) {
            downloadFile("file_$index")
        }
    }
    files.awaitAll()
}

Utilizați dispatcherul IO pentru toate operațiile în care corutina petrece timp în așteptare (I/O-bound). Sarcinile CPU-intensive pe dispatcherul IO sunt ineficiente — ele ocupă fire destinate intrării-ieșirii, reducând capacitatea de transfer a sistemului.

Dispatchers.Default: sarcini CPU-intensive

Dispatchers.Default — dispatcher pentru operații de calcul care încarcă procesorul: sortare, filtrare, parsare JSON (Moshi, Kotlinx Serialization), procesare imagini, calcule. Dimensiunea pool-ului este egală cu numărul de nuclee ale procesorului (dar nu mai puțin de 2). Aceasta asigură încărcarea maximă a CPU fără comutare de context.

kotlin
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
    return withContext(Dispatchers.Default) {
        input
            .parallelStream()
            .map { transform(it) }
            .toList()
    }
}

Nu utilizați Dispatchers.Default pentru operații IO — aceasta va bloca firele pool-ului CPU care ar putea procesa sarcini de calcul. Separarea în IO și Default permite utilizarea optimă a resurselor sistemului: firele IO așteaptă intrare-ieșire, firele CPU sunt ocupate constant cu calcule.

Dispatchers.Unconfined: firul curent

Dispatchers.Unconfined — un dispatcher special care nu leagă corutina de niciun pool. Corutina începe execuția în același fir în care a fost apelată launch/async, iar după suspendare se reia în firul care a apelat resume. Acest comportament este potrivit pentru operații intermediare care nu necesită un context fix.

kotlin
fun main() = runBlocking {
    launch(Dispatchers.Unconfined) {
        println("Înainte de întârziere: ${Thread.currentThread().getName()}")
        delay(500L)
        println("După întârziere: ${Thread.currentThread().getName()}")
    }
}

În codul de producție, Dispatchers.Unconfined este folosit rar. Cazurile principale: transformări ușoare înainte de transmiterea datelor către un alt dispatcher și teste. Pentru sarcinile de producție, utilizați dispatchere explicite — Unconfined este imprevizibil, deoarece firul de execuție depinde de implementarea resume.

Cum să alegi un dispatcher

Alegerea dispatcherului este determinată de tipul sarcinii: operații UI → Main, I/O-bound → IO, CPU-bound → Default, intermediare → moștenire din scope. Pentru Android, se recomandă să porniți corutina pe dispatcherul unde se execută munca principală, iar înainte de actualizarea UI să comutați la Main prin withContext.

  • Nu rulați operații IO pe dispatcherul Main — aceasta blochează UI
  • Nu rulați sarcini CPU-intensive pe dispatcherul IO — aceasta consumă ineficient firele pool-ului IO
  • Utilizați limitedParallelism pentru a controla paralelismul în operații IO masive
  • Comutați dispatcherul cu withContext, nu prin crearea unui nou scope
  • Moșteniți dispatcherul din scope, dacă corutina nu necesită un pool specific

Pentru scenarii complexe, combinați dispatcherele cu operatorul +: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Aceasta creează un CoroutineContext cu dispatcherul specificat, gestionarea erorilor și o ierarhie Job izolată.

Întrebări frecvente

Care este diferența dintre Dispatchers.IO și Dispatchers.Default?

Dispatchers.IO folosește un pool de până la 64 de fire pentru operații I/O-bound (așteptare intrare-ieșire), iar Dispatchers.Default — un pool în funcție de numărul de nuclee CPU pentru sarcini de calcul. La insuficiența firelor, ambele pool-uri pot împărți fire între ele.

Pot crea propriul meu dispatcher în Kotlin Coroutines?

Da, utilizați newSingleThreadContext() pentru unul mono-fir sau newFixedThreadPoolContext() pentru un pool fix. Pentru producție, aplicați limitedParallelism() pe baza dispatcherelor existente — este mai eficient decât crearea de noi pool-uri.

Ce se întâmplă când se apelează Dispatchers.Main într-un fir de fundal?

Dacă Dispatchers.Main nu este disponibil (de exemplu, într-un test JUnit sau serviciu de fundal), se aruncă IllegalStateException. Pentru teste, utilizați TestCoroutineDispatcher, pentru servicii de fundal — Dispatchers.IO sau Default.

Cum limitez numărul de operații IO paralele?

Utilizați Dispatchers.IO.limitedParallelism(N), unde N este numărul maxim de fire paralele. Aceasta previne epuizarea pool-ului la cereri masive și oferă un paralelism controlat.

Când să folosesc Dispatchers.Unconfined?

Dispatchers.Unconfined este potrivit pentru operații intermediare: transformări ușoare de date înainte de transmiterea către un alt dispatcher, scenarii de test. În codul de producție Android nu este recomandat din cauza firului de execuție incert după suspendare.

Rezumat

  • Dispatchers — componente CoroutineContext care determină firele de execuție ale corutinelor
  • Dispatchers.Main — pentru operații UI Android, un fir principal
  • Dispatchers.IO — pentru operații de rețea și disc, pool de până la 64 de fire
  • Dispatchers.Default — pentru calcule CPU-intensive, pool în funcție de numărul de nuclee
  • Dispatchers.Unconfined — fără legare la fir, pentru operații intermediare
  • withContext — mecanismul principal de comutare între dispatchere în cadrul corutinei
  • Alegerea greșită a dispatcherului duce la întârzieri UI sau la utilizarea ineficientă a resurselor

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și