Kotlin Coroutines Асинхронное ожидание последовательности - PullRequest
0 голосов
/ 17 сентября 2018

Не могли бы вы объяснить, в чем разница между этими двумя блоками кода. Первый раз печатает 421, а второй печатает 606. Почему первый параллельный, а второй последовательный?

fun main(args: Array<String>) = runBlocking {

    var time = measureTimeMillis {
        val one = async { one() }
        val two = async { two() }
        val int1 = one.await()
        val int2 = two.await()
        println(int1 + int2)

    }

    println(time)


    time = measureTimeMillis {
        val one = async { one() }.await()
        val two = async { two() }.await()
        println(one + two)

    }

    print(time)
}

suspend fun one(): Int {
    delay(200)
    return 12
}

suspend fun two(): Int {
    delay(400)
    return 23
}

Ответы [ 3 ]

0 голосов
/ 17 сентября 2018
val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()

Что это делает:

  1. вызов задачи один
  2. вызов задачи два
  3. ожидание задачи 1
  4. ожидание задачидва

val one = async { one() }.await()
val two = async { two() }.await()

Что это делает:

  1. задача вызова один
  2. ожидает выполнения задачи один
  3. задача вызовадва
  4. ожидают выполнения задачи два

Здесь нет параллелизма, это чисто последовательный код.Фактически, для последовательного выполнения вы даже не должны использовать async.Правильная идиома

val one = withContext(Dispatchers.Default) { one() }
val two = withContext(Dispatchers.Default) { two() }
0 голосов
/ 18 сентября 2018

После ответа Марко Топольника я попробовал другой вариант, и я думаю, что он принял ответ.Но одна интересная вещь: если я запускаю сопрограмму и не вызываю ожидание, функция запускается, но не заканчивается.Ниже приведен мой код.

fun main(args: Array<String>) = runBlocking {

    var time = measureTimeMillis {
        val one = async { one(1) }
        val two = async { two(1) }
        val int1 = one.await()
        val int2 = two.await()
    }

    println("time: $time")


    time = measureTimeMillis {
        val one = async { one(2) }.await()
        val two = async { two(2) }.await()
    }

    println("time: $time")

    time = measureTimeMillis {
        val one = async { one(3) }
        val two = async { two(3) }
    }

    println("time: $time")



}

suspend fun one(iteration: Int): Int {
    println("func1 start, iteration $iteration")
    delay(200)
    println("func1 end, iteration $iteration")
    return 12
}

suspend fun two(iteration: Int): Int {
    println("func2 start, iteration $iteration")
    delay(400)
    println("func2 end, iteration $iteration")
    return 23
}

И выходные данные:

начало func1, итерация 1
начало func2, итерация 1
конец func1, итерация 1
конец func2, итерация 1
время: 430
начало func1, итерация 2
конец func1, итерация 2
начало func2, итерация 2
конец func2, итерация 2
время: 607
запуск func1, итерация 3
время: 2
запуск func2, итерация 3

Процесс завершен с кодом выхода 0

0 голосов
/ 17 сентября 2018

В первом варианте вы получаете Deferred<Int> для обоих асинхронных вызовов.Как документация Deferred показывает, что есть несколько состояний, в которых может находиться отложенный объект. Снаружи это состояние теперь либо new, либо active, но, конечно, еще не завершено.Однако в вашем втором варианте для первого async-await уже требуется состояние completed, иначе вы не могли бы иметь никакого значения.Однако о вашем async{one()}.await() втором async пока ничего не известно.Также обратите внимание, что возвращаемое значение await() теперь равно Int, а не Deferred, поэтому сопрограмма должна была быть выполнена к тому времени.Проверьте также документацию await().

Другими словами:

val one = async { one() }
val two = async { two() }

И one, и two теперь Deferred<Int>.Ни один еще не был вызван (или, возможно, еще не был вызван).Как только вы позвоните one.await(), он может начать уже и one, и two, просто потому, что у него есть ресурсы для этого (даже если вы нигде не использовали two.await() в своем коде).

Во втором варианте, однако:

val one = async { one() }.await()
val two = async { two() }.await()

Даже если он создает сопрограмму для async {one()}, он должен немедленно установить значение one, потому что вы вызываете await() для него.Типы one и two оба Int.Поэтому, как только будет нажата первая из этих строк, асинхронный код должен быть выполнен немедленно.К тому времени никто не знает, что еще один асинхронный вызов должен быть выполнен, пока мы ожидаем значения первого.Если у первого не будет await, сопрограмма будет снова выполняться параллельно, например:

val one = async { one() }
val two = async { two() }.await()

выполнит one() и two() параллельно.

Так что, возможно, это можно обобщить так: только те сопрограммы могут выполняться параллельно в ожидании, которые к тому времени известны / порождаются.

...