Dispatchers Kotlin Coroutines-da — CoroutineContext komponentlari bo'lib, korutinlarni bajarish uchun iplarni (thread) aniqlaydi: Main (UI-thread), IO (tarmoq va disk), Default (CPU-intensiv vazifalar) va Unconfined (joriy thread). Har bir dispetcher muayyan ish turi uchun optimallashtirilgan ixtisoslashgan thread hovuzini boshqaradi. JetBrains, 2024 qo'llanmasiga ko'ra, to'g'ri dispetcherni tanlash ilovaning ishlashi va barqarorligi uchun juda muhimdir.
Asosiy fikrlar
Dispatchers — CoroutineDispatcher interfeysining implementatsiyalari bo'lib, CoroutineContext elementlaridir. Ular korutina qaysi thread yoki thread hovuzida bajarilishini aniqlaydi. launch yoki async orqali korutina yaratilganda dispetcher birinchi parametr sifatida uzatilishi mumkin: launch(Dispatchers.IO) { ... }. Agar dispetcher ko'rsatilmagan bo'lsa, tashqi CoroutineScope-dan meros qilib olinadi.
Kotlin to'rtta o'rnatilgan dispetcherni taqdim etadi: Main, IO, Default, Unconfined. Har bir dispetcher muayyan operatsiya turi uchun optimallashtirilgan o'z thread hovuzidan foydalanadi. To'g'ri dispetcherni tanlash ilovaning ishlashini belgilaydi: noto'g'ri tanlov interfeys kechikishlariga, CPU yadrolarining bo'sh turishiga yoki threadlarning samarasiz sarflanishiga olib keladi.
| Dispetcher | Thread hovuzi | Maks. thread | Qo'llanilishi |
|---|---|---|---|
| Dispatchers.Main | Bitta (UI) | 1 | UI yangilash, LiveData, View |
| Dispatchers.IO | IO hovuzi | 64 (limitedParallelism) | Tarmoq, fayllar, ma'lumotlar bazasi |
| Dispatchers.Default | CPU hovuzi | N yadro | Saralash, parsering, hisoblashlar |
| Dispatchers.Unconfined | Joriy thread | Yo'q | Oraliq operatsiyalar, testlar |
Dispatchers.Main — korutinalarni Android asosiy threadida bajaradigan dispetcher. Interfeys bilan bog'liq operatsiyalar uchun mo'ljallangan: TextView yangilash, notifyDataSetChanged chaqiruvi, LiveData va StateFlow bilan ishlash. Android-da bu dispetcher Handler (Looper.getMainLooper()) orqali implementatsiya qilingan.
// UI yangilanishlari uchun Main-ga to'g'ri o'tish
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchData()
withContext(Dispatchers.Main) {
_uiState.value = data
}
}
Agar korutina allaqachon Main-dispetcherida bo'lsa, qo'shimcha withContext(Dispatchers.Main) qo'shimcha yuk yaratmaydi — dispetcher joriy threadni tekshiradi va o'tishni o'tkazib yuboradi. withContext dispetcherlar o'rtasida o'tishning afzal ko'rilgan usulidir.
Dispatchers.IO — kirish-chiqish operatsiyalari uchun optimallashtirilgan dispetcher: HTTP so'rovlari (Ktor, OkHttp), fayllarni o'qish va yozish, Room yoki SQLDelight bilan ishlash. U sukut bo'yicha 64 thread dan iborat hovuzdan foydalanadi, yuk ostida masshtablanadi. Har bir yangi IO so'rovi limitga yetguncha qo'shimcha thread yaratishi mumkin.
Bir vaqtda IO operatsiyalari sonini nazorat qilish uchun limitedParallelism() dan foydalaning. Bu funksiya parallel threadlar sonini cheklovchi yangi dispetcher yaratadi, ommaviy operatsiyalarda hovuzning tugashining oldini oladi.
val limitedIo = Dispatchers.IO.limitedParallelism(4)
// 4 ta parallel operatsiya cheklovi bilan 100 ta faylni yuklash
coroutineScope {
val files = (1..100).map { index ->
async(limitedIo) {
downloadFile("file_$index")
}
}
files.awaitAll()
}
Korutina kutish vaqtini o'tkazadigan barcha operatsiyalar (I/O-bound) uchun IO-dispetcheridan foydalaning. IO-dispetcherida CPU-intensiv vazifalar samarasiz — ular kirish-chiqish uchun mo'ljallangan threadlarni egallab, tizimning o'tkazish qobiliyatini pasaytiradi.
Dispatchers.Default — protsessorni yuklaydigan hisoblash operatsiyalari uchun dispetcher: saralash, filtrlash, JSON parseringi (Moshi, Kotlinx Serialization), tasvirlarni qayta ishlash, hisoblashlar. Hovuz hajmi protsessor yadrolari soniga teng (lekin 2 dan kam emas). Bu kontekst almashinuvisiz CPU ning maksimal yuklanishini ta'minlaydi.
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
return withContext(Dispatchers.Default) {
input
.parallelStream()
.map { transform(it) }
.toList()
}
}
IO operatsiyalari uchun Dispatchers.Default dan foydalanmang — bu hisoblash vazifalarini qayta ishlay oladigan CPU hovuzi threadlarini bloklaydi. IO va Default ga bo'lish tizim resurslaridan optimal foydalanish imkonini beradi: IO threadlari kirish-chiqishni kutadi, CPU threadlari doim hisoblashlar bilan band.
Dispatchers.Unconfined — korutinani hech qanday hovuzga bog'lamaydigan maxsus dispetcher. Korutina launch/async chaqirilgan threadda bajarishni boshlaydi va to'xtatilgandan so'ng resume chaqirgan threadda davom etadi. Bu xatti-harakat qat'iy kontekst talab qilmaydigan oraliq operatsiyalar uchun mos keladi.
fun main() = runBlocking {
launch(Dispatchers.Unconfined) {
println("Kechikishdan oldin: ${Thread.currentThread().getName()}")
delay(500L)
println("Kechikishdan keyin: ${Thread.currentThread().getName()}")
}
}
Production kodda Dispatchers.Unconfined kamdan-kam qo'llaniladi. Asosiy holatlar: ma'lumotlarni boshqa dispetcherga o'tkazishdan oldin engil transformatsiyalar va testlar. Production yuki uchun aniq dispetcherlardan foydalaning — Unconfined oldindan aytib bo'lmaydi, chunki bajarish threadi resume implementatsiyasiga bog'liq.
Dispetcher tanlash vazifa turiga qarab belgilanadi: UI operatsiyalari → Main, I/O-bound → IO, CPU-bound → Default, oraliq → scope-dan meros. Android uchun korutinani asosiy ish bajariladigan dispetcherda ishga tushirish va UI yangilashdan oldin withContext orqali Main-ga o'tish tavsiya etiladi.
Murakkab stsenariylar uchun dispetcherlarni + operatori bilan birlashtiring: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Bu belgilangan dispetcher, xato boshqaruvi va izolyatsiya qilingan Job iyerarxiyasi bilan CoroutineContext yaratadi.
Tez-tez beriladigan savollar
Dispatchers.IO I/O-bound operatsiyalari (kirish-chiqish kutish) uchun 64 tagacha thread hovuzidan foydalanadi, Dispatchers.Default esa hisoblash vazifalari uchun CPU yadrolari soniga qarab hovuzdan. Thread yetishmaganda ikkala hovuz bir-biri bilan threadlarni ulashishi mumkin.
Ha, bitta threadli uchun newSingleThreadContext() yoki sobit hovuz uchun newFixedThreadPoolContext() dan foydalaning. Production uchun mavjud dispetcherlar asosida limitedParallelism() qo'llang — bu yangi hovuzlar yaratishdan ko'ra samaraliroq.
Dispatchers.Main mavjud bo'lmasa (masalan, JUnit testida yoki fon xizmatida), IllegalStateException tashlanadi. Testlar uchun TestCoroutineDispatcher, fon xizmatlari uchun Dispatchers.IO yoki Default dan foydalaning.
Dispatchers.IO.limitedParallelism(N) dan foydalaning, bu yerda N — maksimal parallel threadlar soni. Bu ommaviy so'rovlarda hovuzning tugashini oldini oladi va boshqariladigan parallellikni ta'minlaydi.
Dispatchers.Unconfined oraliq operatsiyalar uchun mos keladi: ma'lumotlarni boshqa dispetcherga o'tkazishdan oldin engil transformatsiyalar, test stsenariylari. Android production kodida to'xtatilgandan keyin bajarish threadi noaniq bo'lgani uchun tavsiya etilmaydi.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.