在Kotlin中,如何传递参数以使异步范围保存它?

时间:2017-06-25 14:08:56

标签: async-await closures kotlin coroutine

我有以下使用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)的原始值?

2 个答案:

答案 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的主体。

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

它在中也存在问题,例如:

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