В Kotlin, как передать параметр, чтобы асинхронная область сохранила его?

У меня есть следующий фрагмент кода, использующий 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 .

Что я могу сделать, чтобы исходное значение семенной переменной (которая равно 3) используется внутри области async (вместо 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"); } 

так как вы можете видеть, что последовательность начинается @ выше, которая описана в многопоточных потоках, ясно, что лямбда вызывается лениво. что означает, что тело лямбда не вызывается, если вызывающий не вызывает его.

результат неопределен в многопотоках, возможно, 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 других вызовов inline анонимной функции. вы также можете решить его в kotlin, используя лямбда, как в javascript:

 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).

В вашем конкретном примере вы должны объявить seed как val (неизменный), чтобы предотвратить случайную ошибку, изменив его позже. Фактически, компилятор должен был предупредить вас о том, что вы захватываете изменяемую переменную в сопрограмме coroutine, но эта функция в настоящее время не реализована. Смотрите билет KT-15515 .