Kotlin协程异步等待序列

时间:2018-09-17 13:59:00

标签: kotlin coroutine kotlin-coroutines

您能解释一下这两个代码块之间的区别吗?第一次打印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
}

4 个答案:

答案 0 :(得分:9)

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

这是什么:

  1. 生成任务一
  2. 生成任务二
  3. 等待任务一
  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() }

答案 1 :(得分:1)

在第一个变体中,两个异步调用都获得一个Deferred<Int>。正如the documentation of Deferred很好地显示的那样,延迟对象可能处于几种状态。从外部看,该状态现在是newactive,但肯定还没有完成。但是,在第二个变体上,第一个异步等待已经已经处于completed状态,否则那里就没有任何值。但是,您的async{one()}.await()上的第二个async尚不清楚。 还请注意,await()的返回值现在是Int,现在不再是Deferred,因此协程必须已经执行。还要检查the documentation of await()

换句话说:

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

onetwo现在都为Deferred<Int>。尚未调用任何(或可能尚未调用)。致电one.await()可能会同时启动onetwo,只是因为它拥有所需的资源(即使您没有在任何地方使用two.await()您的代码)。

但是在第二个变体上:

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

即使它为async {one()}创建了一个协程,它也必须立即将一个值设置为one,因为您正在对其调用await()onetwo的类型均为Int。因此,一旦这些行的第一行被击中,就需要立即执行异步代码。到那时,没有人知道在等待第一个异步调用时必须执行另一个异步调用。如果第一个没有await,协程将再次并行执行,例如:

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

将并行执行one()two()

因此,也许可以总结为:只有那些在等待时才知道/产生的协程可以并行执行。

答案 2 :(得分:0)

在Marko Topolnik的答案之后,我尝试了不同的变体,我认为它是可接受的答案。但是一件有趣的事情是,如果我启动协程而不调用await,那么函数将开始但不会结束。下面是我的代码。

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结束的过程

答案 3 :(得分:0)

拇指规则:

  • 在不需要并行执行时使用 withContext
  • 仅在需要并行执行时使用 asyncwithContextasync 都可用于获得启动时无法获得的结果。
  • 使用 withContext 返回单个任务的结果。'
  • 对并行运行的多个任务的结果使用异步。

查看this了解更多详情