在 Kotlin 中,如何传递参数以便异步作用域保存它?
In Kotlin, how do I pass a parameter so that the async scope will conserve it?
我有以下使用 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
.
如何确保在异步范围内使用种子变量的原始值(即 3)(而不是 4)?
让我们看一个非多线程的例子,这样会更清楚:
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 是延迟调用的。这意味着除非调用者调用它,否则不会调用 lambda 的主体。
在多线程中结果不确定,可能是6
也可能是8
,因为要看是序列@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 在 kotlin 中解决它
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
,尤其是在涉及任何形式的并发(协程、线程,甚至将值捕获到 lambda 中)的代码中。
在您的特定示例中,您应该将 seed
声明为 val
(不可变),以防止以后更改它时出现意外错误。事实上,编译器应该警告过您正在将可变变量捕获到协程中,但目前尚未实现此功能。见票KT-15515.
我有以下使用 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
.
如何确保在异步范围内使用种子变量的原始值(即 3)(而不是 4)?
让我们看一个非多线程的例子,这样会更清楚:
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 是延迟调用的。这意味着除非调用者调用它,否则不会调用 lambda 的主体。
在多线程中结果不确定,可能是6
也可能是8
,因为要看是序列@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 在 kotlin 中解决它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
,尤其是在涉及任何形式的并发(协程、线程,甚至将值捕获到 lambda 中)的代码中。
在您的特定示例中,您应该将 seed
声明为 val
(不可变),以防止以后更改它时出现意外错误。事实上,编译器应该警告过您正在将可变变量捕获到协程中,但目前尚未实现此功能。见票KT-15515.