默认顺序调用
顺序调用两个 suspend 函数的时候,两个函数是串行执行的
- 代码
fun main() = runBlocking { val startStamp = System.currentTimeMillis() a() b() log("代码运行总时长:${System.currentTimeMillis()-startStamp}") } suspend fun a(){ delay(2000) log(A) } suspend fun b(){ delay(3000) log(B) } 复制代码
- 日志
日志: A 日志: B 日志: 代码运行总时长:5015 复制代码
- 结论 协程里面两个并行的 suspend 函数会串行执行,呼应开头的结论
使用 async 实现并发
上面例子中我们使用两个 suspend 函数串行执行函数,如果使用两个 async 执行 suspend 方法的话可以实现并发执行的效果。
aysnc 返回一个 Deferred 对象,Deferred 是一个轻量级阻塞式对象,可以调用 Deferred.await()延迟获取返回结果
Deferred 也是一个 Job,所以也可以取消操作
- 代码
fun main() = runBlocking { val startStamp = System.currentTimeMillis() val deferredA=async { a() } val deferredB=async { b() } deferredA.await() deferredB.await() log("执行总时长:${System.currentTimeMillis()-startStamp}") } suspend fun a(){ delay(2000) log(A) } suspend fun b(){ delay(3000) log(B) } 复制代码
- 日志
日志: A 日志: B 日志: 执行总时长:3022 复制代码
- 结论 上一节中代码执行总耗时 5105,本节总耗时 3022。这说明两个 async 是并发执行的
惰性启动的 async
async 可以通过将 start 参数设置为 CoroutineStart.LAZY 而变为惰性的。 在这个模式下,只有结果通过 await 获取的时候协程才会启动,或者在 Job 的 start 函数调用的时候
- 代码
fun main() = runBlocking { val deferred = async (start = CoroutineStart.LAZY){ delay(2000) log(A) "async执行完成" } log(deferred.await()) } 复制代码
- 日志
日志: A 日志: async执行完成 复制代码
- 结论
必须调用 await 或 start 才能启动
使用 async 的结构性并发
协程体作用域中并发的多个 async,有一个发生异常其它的 async 也会被取消
- 代码
fun main() = runBlocking { async { log("吃饭aa,别bb") delay(100) throw IllegalArgumentException("打你一嘴巴子,你再bb") } async { log("就bb,咋地") delay(300) log("bb完了") } log("程序结束") } 复制代码
- 日志
日志: 程序结束 日志: 吃饭aa,别bb 日志: 就bb,咋地 Exception in thread "main" java.lang.IllegalArgumentException: 打你一嘴巴子,你再bb at SuspendMethodKt$main$1$1.invokeSuspend(SuspendMethod.kt:55) 复制代码
- 记录
当第一个 async 抛出异常后,第二个 async 后面 “bb 完了”的日志没有打印,说了第二个 async 也同步被取消了。