使用协程的 Kotlin/Native 多线程

标签 kotlin kotlin-coroutines kotlin-multiplatform kotlin-native

我一直在尝试使用 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 上 pow InvalidMutabilityException。
其原因似乎是传入 withContext 的任何内容都被递归卡住,因为 objectWhichContainsNetworking 是 MainViewModel 的一个属性并在 withContext 中使用,然后 MainViewModel 被卡住。
所以我的问题是,这只是当前 Kotlin/Native 内存模型的限制吗?或者也许是当前版本的协程?有什么办法可以解决这个问题吗?
注意:协程版本:1.3.9-native-mt。 Kotlin 版本 1.4.0。

编辑1:
所以看起来上面的精简代码实际上工作正常。事实证明,有罪的代码是 View 模型中的一个可更新变量(用于保留对最后一个 View 状态的引用),该变量被卡住,然后在尝试改变时抛出异常。我将尝试使用 Flow/Channels 来确保不需要 var 引用,看看这是否能解决整个问题。
注意:如果有一种方法可以避免 MainViewModel 一开始就被卡住,那还是太棒了!

编辑2:
用 Flow 替换了 var。在使用这里的帮助程序之前,我无法在 iOS 中获得标准流收集:https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt .
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
内存模型正在发生变化,但距离它落地还需要很长时间。一旦习惯了内存模型,不可变问题通常很容易诊断。

关于使用协程的 Kotlin/Native 多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63485259/

相关文章:

android - 如何在 Kotlin 中获取 SAM 接口(interface)的对象

android - 为什么我在 android 中需要 ViewModelFactory?

android - Groupie RecyclerView单击特定项目

kotlin - 为什么不推荐使用 Kotlin channel 上的流操作?

android - Kotlin 协程崩溃,没有有用的堆栈跟踪

kotlin - 将 Gradle 子项目与 Kotlin 多平台一起使用

kotlin - 如何从 lambda 引用外部函数?

kotlin - 流量减少操作需要暂停功能

android - Kotlin/Native 将 android 日志导入 android 共享代码模块时遇到问题

gradle - Jitpack和Kotlin跨平台工件groupId