Kotlin / Native Multithreading mit Coroutinen
Ich habe einen Versuch mit der Kotlin-Multiplattform gemacht und sie ist brillant, aber das Einfädeln macht mich fertig. Das Einfrieren des Status zwischen Threads ist konzeptionell sinnvoll und funktioniert gut in einfachen Beispielen, in denen kleine Objekte oder Grundelemente hin und her übergeben werden, aber in realen Anwendungen kann ich InvalidMutabilityException nicht umgehen.
Nehmen Sie das folgende allgemeine Code-Snippet aus einer Android-App
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
}
}
}
Nichts sehr komplexes, aber wenn es in allgemeinem Code verwendet wird und von Kotlin / Native ausgeführt wird, dann pow InvalidMutabilityException auf MainViewModel.
Der Grund dafür scheint zu sein, dass alles, was mit Context übergeben wird, rekursiv eingefroren wird. Da objectWhichContainsNetworking eine Eigenschaft von MainViewModel ist und in withContext verwendet wird, wird MainViewModel beim Einfrieren abgefangen.
Meine Frage ist also, ist dies nur eine Einschränkung des aktuellen Kotlin / Native-Speichermodells? Oder vielleicht die aktuelle Version von Coroutinen? Und gibt es Möglichkeiten, dies zu umgehen?
Hinweis: Coroutinen-Version: 1.3.9-native-mt. Kotlin Version 1.4.0.
Bearbeiten 1: Es scheint also, dass der oben abgespeckte Code tatsächlich gut funktioniert. Es stellt sich heraus, dass der belastende Code eine aktualisierbare Variable im Ansichtsmodell war (die verwendet wird, um einen Verweis auf den letzten Ansichtsstatus beizubehalten), die eingefroren wird und dann die Ausnahme auslöst, wenn versucht wird, mutiert zu werden. Ich werde versuchen, Flow / Channels zu verwenden, um sicherzustellen, dass keine var-Referenz benötigt wird, und um zu sehen, ob dies das Gesamtproblem behebt.
Hinweis: Wenn es eine Möglichkeit gibt, zu verhindern, dass MainViewModel überhaupt eingefroren wird, wäre es immer noch fantastisch!
Bearbeiten 2: Ersetzt die Variable durch Flow. Ich konnte in iOS kein Standard-Flow-Sammeln erhalten, bis ich die Helfer hier verwendet hatte:https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.
MainViewModel wird immer noch eingefroren, aber da sein Zustand unveränderlich ist, ist es kein Problem mehr. Hoffe es hilft jemandem!
Antworten
In Ihrem ursprünglichen Code verweisen Sie auf ein Feld des übergeordneten Objekts, wodurch Sie das gesamte übergeordnete Objekt erfassen und einfrieren. Es ist kein Problem mit Coroutinen. Coroutines folgt denselben Regeln wie alle anderen Parallelitätsbibliotheken in Kotlin / Native. Es friert das Lambda ein, wenn Sie Fäden kreuzen.
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) {}
}
}
Um dies zu verhindern:
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
init{
ensureNeverFrozen()
}
//Etc
Das Komplizierteste am Speichermodell ist dies. Gewöhnen Sie sich an das, was erfasst wird, und vermeiden Sie es. Es ist nicht so schwer, sich daran zu gewöhnen, aber Sie müssen die Grundlagen lernen.
Ich habe ausführlich darüber gesprochen:
Praktische Kotlin / Native Parallelität
Kotlin Native Concurrency Hands On
KotlinConf KN Parallelität
Das Speichermodell ändert sich, aber es wird eine Weile dauern, bis das landet. Sobald Sie sich an das Speichermodell gewöhnt haben, sind die unveränderlichen Probleme im Allgemeinen einfach zu diagnostizieren.