使用协同程序的Kotlin /本机多线程

问题描述

我一直在Kotlin跨平台上开枪,它很棒,但是线程让我感到困惑。线程之间的状态冻结从概念上讲是有意义的,并且在来回传递小对象或基元的简单示例中很好用,但是在现实世界的应用程序中,我无法解决InvalidMutabilityException。

从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
            }
    }
}

没什么复杂的,但是如果在通用代码中使用并从Kotlin / Native运行,则在Mainviewmodel上注入InvalidMutabilityException。

这似乎是因为withContext中传递的所有内容都是递归冻结的,因此因为objectWhichContainsNetworking是Mainviewmodel的属性,并且在withContext中使用,然后Mainviewmodel陷入冻结状态。

所以我的问题是,这仅仅是对当前Kotlin /本机内存模型的限制吗?也许是协程的当前版本?还有什么办法可以解决这个问题?

注意:协程版本:1.3.9-native-mt。 kotlin版本1.4.0。


编辑1: 这样看来,上面精简的代码实际上可以正常工作。事实证明,导致该错误代码是视图模型中的可更新var(用于保留对最后一个视图状态的引用),该变量被冻结,然后在尝试进行突变时引发异常。我将尝试使用“流/通道”以确保不需要var引用,并查看是否可以解决整个问题。

注意:如果有一种方法可以避免Mainviewmodel最初被冻结,那将仍然很棒!


编辑2: 将var替换为Flow。直到在这里使用辅助工具https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt,我才能在iOS中获得标准流程收集。

Mainviewmodel仍然被冻结,但是由于其所有状态都是不可变的,因此不再是问题。希望它能对某人有所帮助!

解决方法

在您的原始代码中,您引用的是父对象的字段,这将导致您捕获整个父对象并将其冻结。协程不是问题。协程遵循与Kotlin / Native中所有其他并发库相同的规则。跨线程时,它将冻结lambda。

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) {}
    }
}

为防止这种情况发生,

class MainViewModel(
    private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
    init{
        ensureNeverFrozen()
    }
    //Etc

内存模型最复杂的事情是这个。习惯于被捕获的内容并避免它。习惯了它并不难,但是您需要学习基础知识。

我已经详细讨论了这一点:

Practical Kotlin/Native Concurrency

Kotlin Native Concurrency Hands On

KotlinConf KN Concurrency

内存模型正在改变,但是要过一段时间才能实现。一旦适应了内存模型,就可以很容易地诊断出不可变的问题。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...