在Kotlin中,我如何传递一个参数,使异步范围能够节约呢?

我有下面的代码片断使用Kotlin协程

fun main(args:Array<String>){ println("test") var seed = 3 val deferredResult = async(CommonPool){ seed * 2 } seed = 4 runBlocking(CommonPool) { val result = deferredResult.await() println("Result is $result") } println("end") } 

我期待它的行为像JavaScript,并在定义协程时保存seed变量(使用副本)的价值。 但不是打印Result is 6 ,它打印Result is 8

我能做些什么来确保在异步范围(而不是4)内使用seed变量(即3)的原始值?

让我们看看一个非多线程的例子,这将使它更清楚:

 fun main(args:Array<String>){ println("test") var seed = 3 // @1. initializing seed=3 val deferredResult = { seed * 2 // @4. seed=4 then } seed = 4 // @2. reassign seed=4 // v--- @3. calculates the result val result = deferredResult() // ^--- 8 println("Result is $result"); } 

正如你可以看到在非多线程中所描述的序列开始@清楚地说明了拉姆达被懒惰地调用。 这意味着lambda的主体不被调用,除非调用者调用它。

在多线程中结果是不确定的,它可能是68 ,因为它取决于它是序列@2还是序列@4被首先调用。 当我们调用async(..)在池中启动一个线程时,会花费几次时间,直到线程运行,当前线程才会被阻塞。

它在javascript中也有问题,例如:

 var seed = 3 function deferredResult() { return seed * 2 } seed = 4 var result = deferredResult() console.log("Result is " + result);// result is also 8 

它是通过在javascript中引入另一个调用内联匿名函数来解决的。 你也可以通过在javascript中使用lambda来解决它:

 fun main(args: Array<String>) { println("test") var seed = 3 // v--- like as javascript (function(seed){...})(seed); val deferredResult = ({ seed: Int -> async(CommonPool) { seed * 2 } })(seed); seed = 4 runBlocking(CommonPool) { val result = deferredResult.await() // ^--- result is always 6 now println("Result is $result") } println("end") } 

您应该避免使用var ,特别是在涉及任何形式的并发(代码,协程,线程,甚至将值捕获到lambdas中)的代码中。

在你的具体例子中,你应该声明seedval (不可变),以防止后来改变它的意外错误。 事实上,编译器应该已经警告过你正在捕获一个可变变量到协程,但是这个功能目前还没有实现。 见票KT-15515 。