Coroutines kullanarak Kotlin / Native multithreading
Kotlin çoklu platformunda bir şansım var ve bu harika, ama iş parçacığı beni şaşırtıyor. İplikler arasındaki durumun donması kavramsal olarak mantıklıdır ve küçük nesnelerin veya ilkellerin ileri geri aktarıldığı basit örneklerde gayet iyi çalışır, ancak gerçek dünya uygulamalarında InvalidMutabilityException'ın üstesinden gelemiyorum.
Bir Android uygulamasından aşağıdaki ortak kod parçacığını alın
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
}
}
}
Çok karmaşık bir şey değil, ancak ortak kodda kullanılırsa ve Kotlin / Native'den çalıştırılırsa, MainViewModel'de InvalidMutabilityException'ı çalıştırın.
Görünüşe göre bunun nedeni, withContext'e iletilen her şeyin özyinelemeli olarak dondurulmasıdır, çünkü objectWhichContainsNetworking, MainViewModel'in bir özelliğidir ve withContext'te kullanılır, ardından MainViewModel donmaya yakalanır.
Öyleyse sorum şu, bu sadece mevcut Kotlin / Yerel bellek modelinin bir sınırlaması mı? Veya belki de eşgüdümlerin güncel sürümü? Ve bunun etrafında herhangi bir yol var mı?
Not: coroutines sürümü: 1.3.9-native-mt. kotlin sürüm 1.4.0.
Düzenleme 1: Görünüşe göre yukarıdaki zayıflatılmış kod aslında iyi çalışıyor. Suçlayıcı kodun, görünüm modelinde (son görünüm durumuna bir referans tutmak için kullanılan) güncellenebilir bir değişken olduğu ve donmuş hale geldiği ve ardından mutasyona uğratılmaya çalıştığında istisnayı attığı ortaya çıktı. Değişken referansına gerek olmadığından emin olmak için Akış / Kanalları kullanmaya çalışacağım ve bunun genel sorunu çözüp çözmediğine bakacağım.
Not: MainViewModel'in ilk etapta donmasını önlemenin bir yolu varsa, yine de harika olurdu!
Düzenleme 2: Var, Flow ile değiştirildi. Aşağıdaki yardımcıları kullanana kadar iOS'ta standart akış toplama alamadım:https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.
MainViewModel hala donuyor, ancak tüm durumu değişmez olduğu için artık bir sorun değil. Umarım birine yardımcı olur!
Yanıtlar
Orijinal kodunuzda, ana nesnenin bir alanına referans veriyorsunuz, bu da tüm ebeveyni yakalamanıza ve dondurmanıza neden olur. Coroutine'lerle ilgili bir sorun değil. Coroutines, Kotlin / Native'deki diğer tüm eşzamanlılık kitaplıklarıyla aynı kuralları izler. İplikleri geçtiğinizde lambdayı dondurur.
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) {}
}
}
Bunun olmasını önlemek için:
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
init{
ensureNeverFrozen()
}
//Etc
Bellek modeliyle ilgili en karmaşık şey budur. Yakalananlara alışmak ve ondan kaçınmak. Alıştığınızda o kadar da zor değil ama temelleri öğrenmeniz gerekiyor.
Bunun hakkında uzun uzun konuştum:
Pratik Kotlin / Yerel Eş Zamanlılık
Kotlin Native Concurrency Uygulamalı
KotlinConf KN Eşzamanlılığı
Hafıza modeli değişiyor, ancak bunun gerçekleşmesi epey zaman alacak. Bellek modeline alıştığınızda, değişmez sorunları teşhis etmek genellikle kolaydır.