2

我在这里关注 Kotlin Hands On for Kotlin/Native Concurrency 。我无法理解最后一个示例,为什么“上面修改后的 saveToDb 函数现在处理后台调用,并且只捕获函数参数。这不会冻结父类

class CountingModelSafer{
    var count = 0

    fun increment(){
        count++
        saveToDb(count)
    }

    private fun saveToDb(arg:Int) = background {
        println("Doing db stuff with $arg, in main $isMainThread")
    }
}

[请注意,在这些示例中,在后台,传递的 lambda 被冻结]

下面的代码片段导致整个 CountingModel 被冻结,但不是上面的代码片段。有人可以帮我理解为什么会这样吗?

class CountingModel{
    var count = 0

    fun increment(){
        count++
        background {
            saveToDb(count)
        }
    }

    private fun saveToDb(arg:Int){
        //Do some db stuff
        println("Saving $arg to db")
    }
}
4

1 回答 1

4

当然。我写了示例,所以让我尝试解释一下。

        background {
            saveToDb(count)
        }

background函数接受一个 lambda 参数。Lambda 可以“捕获”状态并影响它。我的意思是,忘记“冻结”,您通常会期望更改的值count会更改源值。因此,如果我们忘记“冻结”,如果count从 0 开始,则以下结果count为 1:

        background {
            count++
        }

为了实现这一点,lambda 需要引用count,并且引用 tocount意味着您将对持有它的类表示敬意。更正式地说,代码应如下所示:

        background {
            this.count++
        }

我们不需要声明this.count,而只是因为this是假设的。

Lambda 需要访问它们引用的状态,而这一切都不是魔法。Lambda 具有状态,即它们捕获的对象。冻结状态是指被冻结者接触到的所有状态。对于 lambda 函数,这意味着代码引用的任何内容。

在这种情况下:

    private fun saveToDb(arg:Int) = background {
        println("Doing db stuff with $arg, in main $isMainThread")
    }

arg:Int是一个参数。arg被冻结,但 lambda 中没有任何内容捕获父类。如果你写this.arg,那将是一个错误。arg是本地的。冻结它不会级联到父类。

我开始编写一个 Intellij/AS 插件,它可以帮助警告您这些可能的情况,因为这是最有可能绊倒人们的事情,但 Kotlin 团队告诉我,他们已经决定这种内存模型将消失。因此,该插件的工作停止了。在明年的某个时候,这将不是问题。

然而,学习这种记忆模型是有用的。从线程的角度来看,开发人员倾向于随意做一些危险的事情。Kotlin/Native 限制性线程模型迫使您重新思考如何构建代码。这不一定是坏事。

但我离题了。模型正在改变。同时,请注意捕获的状态。

于 2021-06-19T03:17:51.830 回答