Dispatcher adalah bagian dari konteks coroutine yang menentukan thread mana yang menjalankan kode coroutine, dan karenanya menjadi tempat keputusan penempatan pekerjaan diambil. Topik ini menguraikan dispatcher bawaan Dispatchers.Default, Dispatchers.IO, dan Dispatchers.Main, menunjukkan perpindahan konteks dengan withContext, serta menutup dengan praktik penempatan pekerjaan pada platform berantarmuka grafis.
Dispatcher bawaan#
Library coroutines menyediakan tiga dispatcher bawaan yang masing-masing dirancang untuk jenis pekerjaan tertentu. Dispatchers.Default berisi pool thread yang ukurannya mengikuti jumlah inti prosesor dan ditujukan untuk pekerjaan berhitung berat seperti pengurutan data besar atau pengolahan gambar. Dispatchers.IO berisi pool yang lentur dengan jumlah thread yang lebih banyak dan ditujukan untuk pekerjaan yang menunggu, misalnya membaca berkas atau memanggil jaringan, karena thread pada pekerjaan semacam itu sering diam menunggu.
Dispatcher ketiga, Dispatchers.Main, menempatkan coroutine pada thread antarmuka pengguna. Dispatcher ini tidak ada pada JVM polos dan baru tersedia setelah library integrasi platformnya disertakan, misalnya pada aplikasi Android atau aplikasi desktop berbasis JavaFX. Karena itu kode yang merujuk Dispatchers.Main hanya dapat dijalankan pada platform yang menyediakannya, dan contoh pada topik ini tidak menjalankannya di terminal.
| Dispatcher | Isi pool | Pekerjaan yang cocok |
|---|---|---|
Dispatchers.Default | Thread sebanyak inti prosesor | Perhitungan berat, pengurutan, transformasi data |
Dispatchers.IO | Pool lentur dengan thread yang lebih banyak | Berkas, jaringan, panggilan yang menunggu |
Dispatchers.Main | Thread antarmuka pengguna | Pembaruan layar, hanya pada platform UI |
Pemisahan dua pool pertama mencegah kedua jenis pekerjaan saling merebut thread. Pool Default yang kecil menjaga perhitungan berat tetap memakai seluruh inti prosesor secara efisien, sedangkan pool IO yang besar menampung banyak thread yang sibuk menunggu. Apabila keduanya digabung, pekerjaan menunggu bisa menghabiskan thread yang dibutuhkan perhitungan, atau sebaliknya perhitungan berat menyita thread untuk pemrosesan masukan.
Perpindahan konteks dengan withContext#
Fungsi withContext menjeda coroutine yang sedang berjalan, menjalankan bloknya pada dispatcher yang dituju, lalu mengembalikan eksekusi ke konteks sebelumnya dengan seluruh hasil blok sebagai nilai kembaliannya. Perpindahan itu terjadi tanpa membuat coroutine baru, sehingga keadaan variabel lokal tetap utuh dan kode tetap terbaca sekuensial.
Program berikut membuktikan perpindahan itu dengan thread yang diberi nama sendiri. Thread pemberi nama sendiri dipilih agar keluarannya stabil, karena nama thread milik pool bawaan berubah antarjalankan dan tidak layak dijadikan acuan.
import kotlinx.coroutines.*
import java.util.concurrent.Executors
fun main() = runBlocking {
val dispatcherLaporan = Executors.newSingleThreadExecutor { tugas ->
Thread(tugas, "ctx-laporan-1")
}.asCoroutineDispatcher()
println("Awal pada thread: ${Thread.currentThread().name}")
withContext(dispatcherLaporan) {
println("Memuat laporan pada thread: ${Thread.currentThread().name}")
}
println("Kembali pada thread: ${Thread.currentThread().name}")
dispatcherLaporan.close()
}# compiler Kotlin dan library coroutines yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export LIBS=/home/kal/my-project/tools/libs
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi main.kt dengan library coroutines pada classpath, lalu jalankan programnya
kotlinc main.kt -cp "$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar" -d out.jar && java -cp "out.jar:$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar:$STDLIB" MainKtAwal pada thread: main
Memuat laporan pada thread: ctx-laporan-1
Kembali pada thread: mainKeluarannya mengikuti jalur perpindahan dengan tepat. runBlocking menjalankan isinya pada thread main, kemudian blok withContext dieksekusi pada thread ctx-laporan-1 milik executor tunggal, dan setelah blok selesai eksekusi kembali ke thread main. runBlocking memang jembatan yang berjalan di thread main, sedangkan coroutine di dalamnya dapat berpindah thread berkali-kali tanpa menyadari perpindahan itu.
Dispatchers bawaan dapat dipakai dengan pola yang sama, misalnya withContext(Dispatchers.IO) { ... } di sekitar pembacaan berkas. Bedanya, nama thread milik pool bawaan tidak stabil untuk dicetak, sehingga bukti perpindahan pada contoh ini sengaja memakai executor tunggal bernama.
Penempatan pekerjaan pada platform UI#
Pada aplikasi berantarmuka grafis, keputusan penempatan pekerjaan mengikuti satu aturan, yaitu pembaruan layar hanya boleh dilakukan dari thread UI, sedangkan pekerjaan yang menunggu ditempatkan di tempat lain. Pola yang lazim adalah memuat data pada Dispatchers.IO dan menampilkan hasilnya pada Dispatchers.Main, sebagaimana pada potongan kode berikut.
// tidak-dijalankan
// Dispatchers.Main hanya tersedia pada platform UI, misalnya Android
suspend fun tampilkanLaporan() {
val data = withContext(Dispatchers.IO) {
muatLaporan()
}
withContext(Dispatchers.Main) {
tampilkan(data)
}
}Pola tersebut terbaca sebagai satu urutan lurus, padahal di baliknya coroutine berpindah thread dua kali, dari thread UI ke pool IO lalu kembali ke thread UI. Selama proses memuat, thread UI tetap bebas menggambar layar dan menanggapi sentuhan, sehingga antarmuka tidak pernah macet. Pola inilah alasan utama dispatcher dipisah-pisah, yaitu menjaga thread yang kritis tetap ringan.
Ringkasan#
- Dispatcher menentukan thread yang menjalankan coroutine, dan pilihan pemakaiannya adalah keputusan penempatan pekerjaan.
Dispatchers.Defaultberisi thread sebanyak inti prosesor untuk perhitungan berat,Dispatchers.IOberisi pool lentur untuk pekerjaan menunggu, sedangkanDispatchers.Mainhanya ada pada platform UI.withContextmenjalankan blok pada dispatcher tujuan lalu kembali ke konteks semula dengan nilai kembalian bloknya.- Pada platform UI, data dimuat pada
Dispatchers.IOdan hasilnya ditampilkan padaDispatchers.Mainagar thread UI tetap bebas.
Referensi#
- Coroutine context and dispatchers — JetBrains — Dokumentasi resmi dispatcher, withContext, dan penjelasan konteks coroutine.
- Coroutines guide — JetBrains — Panduan resmi coroutines Kotlin sebagai peta seluruh topik pada modul ini.