我有以下使用Kotlin Coroutines的代码片段
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)内使用种子变量(3)的原始值?
答案 0 :(得分:2)
让我们看一个非多线程的例子,它会使它更清晰:
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")
}
答案 1 :(得分:1)
您应该避免使用// update sub menus
case MOVE_SUB_MENU: {
let newCustomMenus = state.mainMenus ? [...state.mainMenus] : []
let newSubMenus = [...newCustomMenus[action.mainMenuIndex].sub_button]
if (action.to || action.to === 0) {
newSubMenus.splice(action.to, 0, newSubMenus.splice(action.from, 1)[0])
newCustomMenus[action.mainMenuIndex].sub_button = newSubMenus
} else {
newSubMenus.splice(action.from, 1)
newCustomMenus[action.mainMenuIndex].sub_button = newSubMenus
}
return Object.assign({}, state, { mainMenus: newCustomMenus })
}
// update main menus
case MOVE_MAIN_MENU: {
let newCustomMenus = state.mainMenus ? [...state.mainMenus] : []
if (action.to || action.to === 0) {
newCustomMenus.splice(action.to, 0, newCustomMenus.splice(action.from, 1)[0])
} else {
newCustomMenus.splice(action.from, 1)
}
return Object.assign({}, state, { mainMenus: newCustomMenus })
}
,尤其是在涉及任何形式的并发(协程,线程,甚至将值捕获到lambdas中)的代码中。
在您的特定示例中,您应该将var
声明为seed
(不可变),以防止以后意外更改错误。事实上,编译器应该警告您正在将可变变量捕获到协程中,但是此功能目前尚未实现。查看机票KT-15515。