Dispatchers, Kotlin Coroutines'de CoroutineContext bileşenleridir ve coroutine'lerin yürütüleceği iş parçacıklarını belirler: Main (UI iş parçacığı), IO (ağ ve disk), Default (CPU yoğun görevler) ve Unconfined (mevcut iş parçacığı). Her dağıtıcı, belirli bir çalışma türü için optimize edilmiş uzmanlaşmış bir iş parçacığı havuzunu yönetir. JetBrains rehberi, 2024'e göre, doğru dağıtıcıyı seçmek uygulama performansı ve kararlılığı için kritiktir.
Önemli Noktalar
Dispatchers, CoroutineDispatcher arayüzünün uygulamalarıdır ve CoroutineContext'in öğeleridir. Coroutine'in hangi iş parçacığında veya iş parçacığı havuzunda yürütüleceğini belirlerler. launch veya async aracılığıyla bir coroutine oluştururken, dağıtıcı ilk parametre olarak iletilebilir: launch(Dispatchers.IO) { ... }. Hiçbir dağıtıcı belirtilmezse, dış CoroutineScope'tan devralınır.
Kotlin dört yerleşik dağıtıcı sağlar: Main, IO, Default, Unconfined. Her dağıtıcı, belirli bir işlem türü için optimize edilmiş kendi iş parçacığı havuzunu kullanır. Doğru dağıtıcıyı seçmek uygulama performansını belirler: yanlış seçim UI gecikmelerine, boşta CPU çekirdeklerine veya verimsiz iş parçacığı kullanımına yol açar.
| Dağıtıcı | İş Parçacığı Havuzu | Maks. İş Parçacığı | Kullanım |
|---|---|---|---|
| Dispatchers.Main | Bir (UI) | 1 | UI güncellemeleri, LiveData, View |
| Dispatchers.IO | IO havuzu | 64 (limitedParallelism) | Ağ, dosyalar, DB |
| Dispatchers.Default | CPU havuzu | N çekirdek | Sıralama, ayrıştırma, hesaplamalar |
| Dispatchers.Unconfined | Geçerli iş parçacığı | Yok | Ara işlemler, testler |
Dispatchers.Main, coroutine'leri Android ana iş parçacığında yürüten dağıtıcıdır. UI ile ilgili işlemler için tasarlanmıştır: TextView güncelleme, notifyDataSetChanged çağırma, LiveData ve StateFlow ile çalışma. Android'de bu dağıtıcı, Handler (Looper.getMainLooper()) aracılığıyla uygulanır.
// Correct switch to Main for UI updates
viewModelScope.launch(Dispatchers.IO) {
val data = repository.fetchData()
withContext(Dispatchers.Main) {
_uiState.value = data
}
}
Bir coroutine zaten Main dağıtıcısındaysa, ek withContext(Dispatchers.Main) ek yük oluşturmaz — dağıtıcı geçerli iş parçacığını kontrol eder ve değiştirmeyi atlar. withContext, dağıtıcılar arasında geçiş yapmanın tercih edilen yoludur.
Dispatchers.IO, G/Ç işlemleri için optimize edilmiş bir dağıtıcıdır: HTTP istekleri (Ktor, OkHttp), dosya okuma ve yazma, Room veya SQLDelight ile çalışma. Varsayılan olarak 64 iş parçacığından oluşan bir havuz kullanır, yük altında ölçeklenebilir. Her yeni G/Ç isteği, sınıra ulaşılana kadar ek bir iş parçacığı oluşturabilir.
Eşzamanlı G/Ç işlemlerinin sayısını kontrol etmek için limitedParallelism() kullanın. Bu işlev, paralel iş parçacığı sayısında bir sınırla yeni bir dağıtıcı oluşturarak toplu işlemler sırasında havuzun tükenmesini önler.
val limitedIo = Dispatchers.IO.limitedParallelism(4)
// Load 100 files with limit of 4 concurrent operations
coroutineScope {
val files = (1..100).map { index ->
async(limitedIo) {
downloadFile("file_$index")
}
}
files.awaitAll()
}
Coroutine'in bekleyerek zaman geçirdiği (G/Ç bağlamlı) tüm işlemler için IO dağıtıcısını kullanın. IO dağıtıcısındaki CPU yoğun görevler verimsizdir — G/Ç için ayrılmış iş parçacıklarını işgal ederek sistemin verimini düşürürler.
Dispatchers.Default, işlemciyi yükleyen hesaplama işlemleri için dağıtıcıdır: sıralama, filtreleme, JSON ayrıştırma (Moshi, Kotlinx Serialization), görüntü işleme, hesaplamalar. Havuz boyutu, işlemci çekirdeği sayısına eşittir (ancak 2'den az değil). Bu, bağlam değiştirme olmadan maksimum CPU kullanımını sağlar.
suspend fun processData(input: List<RawRecord>): List<ProcessedRecord> {
return withContext(Dispatchers.Default) {
input
.parallelStream()
.map { transform(it) }
.toList()
}
}
G/Ç işlemleri için Dispatchers.Default kullanmayın — bu, hesaplama görevlerini işleyebilecek CPU havuzu iş parçacıklarını bloke eder. IO ve Default'u ayırmak, sistem kaynaklarının optimum kullanımını sağlar: IO iş parçacıkları G/Ç bekler, CPU iş parçacıkları sürekli hesaplamalarla meşgul olur.
Dispatchers.Unconfined, bir coroutine'i herhangi bir havuza bağlamayan özel bir dağıtıcıdır. Coroutine, launch/async'in çağrıldığı iş parçacığında yürütmeye başlar ve askıya alındıktan sonra resume'u çağıran iş parçacığında devam eder. Bu davranış, sabit bir bağlam gerektirmeyen ara işlemler için uygundur.
fun main() = runBlocking {
launch(Dispatchers.Unconfined) {
println("Before delay: ${Thread.currentThread().getName()}")
delay(500L)
println("After delay: ${Thread.currentThread().getName()}")
}
}
Üretim kodunda Dispatchers.Unconfined nadiren kullanılır. Ana kullanım durumları: verileri başka bir dağıtıcıya aktarmadan önce hafif dönüşümler ve testler. Üretim iş yükleri için açık dağıtıcılar kullanın — Unconfined öngörülemezdir çünkü yürütme iş parçacığı resume uygulamasına bağlıdır.
Dağıtıcı seçimi görev türüne bağlıdır: UI işlemleri → Main, G/Ç bağlamlı → IO, CPU bağlamlı → Default, ara → kapsamdan devralma. Android için, coroutine'in ana çalışmanın yapıldığı dağıtıcıda başlatılması ve UI'yi güncellemeden önce withContext aracılığıyla Main'e geçilmesi önerilir.
Karmaşık senaryolar için, + operatörünü kullanarak dağıtıcıları birleştirin: Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler. Bu, belirtilen bir dağıtıcı, hata işleme ve izole bir Job hiyerarşisi ile bir CoroutineContext oluşturur.
Sıkça Sorulan Sorular
Dispatchers.IO, G/Ç bağlamlı işlemler (G/Ç bekleme) için 64 iş parçacığına kadar bir havuz kullanırken, Dispatchers.Default hesaplama görevleri için CPU çekirdeği sayısına göre bir havuz kullanır. İş parçacıkları kıt olduğunda, her iki havuz da birbiriyle iş parçacığı paylaşabilir.
Evet, tek iş parçacıklı için newSingleThreadContext() veya sabit havuz için newFixedThreadPoolContext() kullanın. Üretim için, mevcut dağıtıcılara dayalı limitedParallelism() kullanın — bu, yeni havuzlar oluşturmaktan daha verimlidir.
Dispatchers.Main kullanılamıyorsa (örneğin, bir JUnit testinde veya arka plan hizmetinde), bir IllegalStateException fırlatılır. Testler için TestCoroutineDispatcher ve arka plan hizmetleri için Dispatchers.IO veya Default kullanın.
Dispatchers.IO.limitedParallelism(N) kullanın, burada N maksimum paralel iş parçacığı sayısıdır. Bu, toplu istekler sırasında havuzun tükenmesini önler ve kontrollü paralellik sağlar.
Dispatchers.Unconfined ara işlemler için uygundur: başka bir dağıtıcıya aktarmadan önce hafif veri dönüşümleri, test senaryoları. Üretim Android kodunda, askıya alındıktan sonra tanımsız yürütme iş parçacığı nedeniyle önerilmez.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun