Kotlin / Native multithreading menggunakan coroutine
Saya telah mencoba multiplatform kotlin dan itu brilian, tetapi threading membuat saya bingung. Pembekuan status antara utas masuk akal secara konseptual, dan berfungsi dengan baik dalam contoh sederhana di mana objek kecil atau primitif diteruskan bolak-balik, tetapi dalam aplikasi dunia nyata saya tidak bisa mengatasi InvalidMutabilityException.
Ambil cuplikan kode umum berikut dari aplikasi android
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
private var coreroutineSupervisor = SupervisorJob()
private var coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Main + coreroutineSupervisor)
private fun loadResults() {
// Here: Show loading
coroutineScope.launch {
try {
val result = withContext(Dispatchers.Default) { objectWhichContainsNetworking.fetchData() }
// Here: Hide loading and show results
} catch (e: Exception) {
// Here: Hide loading and show error
}
}
}
Tidak ada yang sangat rumit, tetapi jika digunakan dalam kode umum dan dijalankan dari Kotlin / Native maka pow InvalidMutabilityException di MainViewModel.
Tampaknya alasan untuk ini adalah bahwa apa pun yang diteruskan dalam withContext dibekukan secara rekursif sehingga karena objectWhichContainsNetworking adalah properti dari MainViewModel dan digunakan dalam withContext maka MainViewModel akan terjebak dalam pembekuan.
Jadi pertanyaan saya adalah, apakah ini hanya batasan dari model memori Kotlin / Native saat ini? Atau mungkin versi coroutine saat ini? Dan apakah ada cara untuk mengatasi ini?
Catatan: versi coroutines: 1.3.9-native-mt. kotlin versi 1.4.0.
Sunting 1: Jadi, tampaknya kode yang diperkecil di atas benar-benar berfungsi dengan baik. Ternyata kode yang memberatkan adalah var yang dapat diperbarui dalam model tampilan (digunakan untuk menyimpan referensi ke status tampilan terakhir) yang menjadi beku dan kemudian memunculkan pengecualian ketika mencoba dimutasi. Saya akan mencoba menggunakan Flow / Channels untuk memastikan tidak ada referensi var yang dibutuhkan dan melihat apakah ini memperbaiki masalah secara keseluruhan.
Catatan: jika ada cara untuk menghindari MainViewModel dibekukan sejak awal, itu akan tetap luar biasa!
Edit 2: Mengganti var dengan Flow. Saya tidak bisa mendapatkan pengumpulan aliran standar di iOS sampai menggunakan bantuan di sini:https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.
MainViewModel masih dibekukan, tetapi karena semua statusnya tidak berubah, itu tidak lagi menjadi masalah. Semoga membantu seseorang!
Jawaban
Dalam kode asli Anda, Anda mereferensikan bidang objek induk, yang menyebabkan Anda menangkap seluruh induk dan membekukannya. Ini bukan masalah dengan coroutine. Coroutine mengikuti aturan yang sama seperti semua library konkurensi lainnya di Kotlin / Native. Ini membekukan lambda saat Anda melewati utas.
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
//yada yada
private fun loadResults() {
coroutineScope.launch {
try {
val result = withContext(Dispatchers.Default) {
//The reference to objectWhichContainsNetworking is a field ref and captures the whole view model
objectWhichContainsNetworking.fetchData()
}
} catch (e: Exception) {}
}
}
Untuk mencegah hal ini terjadi:
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
init{
ensureNeverFrozen()
}
//Etc
Hal yang paling rumit dengan model memori adalah ini. Membiasakan diri dengan apa yang ditangkap dan menghindarinya. Tidak terlalu sulit jika Anda sudah terbiasa, tetapi Anda perlu mempelajari dasar-dasarnya.
Saya telah membicarakan hal ini secara panjang lebar:
Kotlin / Native Concurrency Praktis
Kotlin Native Concurrency Hands On
KotlinConf KN Concurrency
Model memori berubah, tetapi itu akan memakan waktu cukup lama sebelum itu mendarat. Setelah Anda terbiasa dengan model memori, masalah yang tidak dapat diubah biasanya langsung didiagnosis.