Kotlin/Native 使用协程的多线程
Kotlin/Native multithreading using coroutines
我一直在尝试使用 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。科特林版本 1.4.0.
编辑 1:
所以看起来上面精简的代码实际上工作正常。事实证明,罪魁祸首的代码是视图模型中的一个可更新变量(用于保留对最后一个视图状态的引用),它会被冻结,然后在它试图改变时抛出异常。我将尝试使用 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
内存模型正在发生变化,但还需要很长时间才能实现。一旦习惯了内存模型,不可变问题通常很容易诊断。
我一直在尝试使用 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。科特林版本 1.4.0.
编辑 1: 所以看起来上面精简的代码实际上工作正常。事实证明,罪魁祸首的代码是视图模型中的一个可更新变量(用于保留对最后一个视图状态的引用),它会被冻结,然后在它试图改变时抛出异常。我将尝试使用 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
内存模型正在发生变化,但还需要很长时间才能实现。一旦习惯了内存模型,不可变问题通常很容易诊断。