Kotlin Coroutines异步等待序列

tof*_*for 5 coroutine kotlin 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
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*nik 15

val one = async { one() }
val two = async { two() }
val int1 = one.await()
val int2 = two.await()
Run Code Online (Sandbox Code Playgroud)

这是做什么的:

  1. 产卵任务一
  2. 产卵任务二
  3. 等待任务一
  4. 等待任务二

val one = async { one() }.await()
val two = async { two() }.await()
Run Code Online (Sandbox Code Playgroud)

这是做什么的:

  1. 产卵任务一
  2. 等待任务一
  3. 产卵任务二
  4. 等待任务二

这里没有并发性,它是纯粹的顺序代码.实际上,对于顺序执行,您甚至不应该使用async.正确的习语是

val one = withContext(Dispatchers.Default) { one() }
val two = withContext(Dispatchers.Default) { two() }
Run Code Online (Sandbox Code Playgroud)

  • `withContext` 片段和简单的 `val one = one() 有什么区别?值二 = 二()`? (2认同)
  • 我们假设`one()`和`two()`是阻塞函数,并且调用线程具有事件循环(最典型的是UI线程)。仅编写`one()`会阻塞当前线程,而编写`withContext(Default){one()}`会将阻塞调用转移到线程池,而当前线程可以继续处理其他事件,直到`withContext()`的结果为止。准备好了。 (2认同)
  • 请注意,即使代码是顺序执行的,它也不会始终在同一个线程上执行。像这样的协程在线程之间跳转与线程在 CPU 核心之间跳转非常相似。线程之于协程就像 CPU 内核之于线程(模抢占式调度)。 (2认同)

Rol*_*and 9

在第一个变体中,您会得到Deferred<Int>两个异步调用的 a 。正如的文档Deferred很好地显示的那样,延迟对象可能处于几种状态。从外部来看,该状态现在要么是new要么,active但肯定还没有完成。然而,在您的第二个变体中,第一个异步等待completed已经需要一个状态,否则您在那里无法获得任何值。然而,你的async{one()}.await()第二个async还不知道。另请注意, 的返回值await()是现在Int,不再Deferred是,因此此时协程一定已经执行。另请检查的文档await()

换句话说:

val one = async { one() }
val two = async { two() }
Run Code Online (Sandbox Code Playgroud)

现在one都是和。尚未调用任何内容(或可能已调用)。一旦您调用它,它可能就已经启动了和,只是因为它拥有它的资源(即使您没有在代码中的任何地方使用)。twoDeferred<Int>one.await()onetwotwo.await()

然而,对于第二种变体:

val one = async { one() }.await()
val two = async { two() }.await()
Run Code Online (Sandbox Code Playgroud)

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

val one = async { one() }
val two = async { two() }.await()
Run Code Online (Sandbox Code Playgroud)

将并行one()执行。two()

因此,也许这可以总结为:只有那些协程可以在等待时并行执行,那时它们是已知的/生成的。


Moh*_*ush 7

拇指规则:

  • 当不需要并行执行时使用withContext 。
  • 仅当需要并行执行时才使用异步。withContextasync都可以用来获取启动时无法获得的结果。
  • 使用withContext返回单个任务的结果。
  • 对并行运行的多个任务的结果使用异步。

检查以了解更多详细信息