Kotlin-通过async与await实现高效并发
2021-06-15 本文已影响0人
蒋斌文
Kotlin-通过async与await实现高效并发
image-20210615150418309/*
挂起函数的组合
*/
fun main() = runBlocking {
val elapsedTime = measureTimeMillis {
val value1 = intValue1()
val value2 = intValue2()
println("$value1 + $value2 = ${value1 + value2}")
}
println("total time: $elapsedTime")
}
private suspend fun intValue1(): Int {
delay(1000)
return 15
}
private suspend fun intValue2(): Int {
delay(2000)
return 20
}
RUN> 🏂🏂🏂🏂🏂🏂
15 + 20 = 35 total time: 3010 Process finished with exit code 0
总耗时3秒多(2+1),说明程序是串行执行的
这俩函数之间是没有任何依赖关系的,那如果有一种机制能在执行intValue1的同时,也能并行的执行intValue2,那最终总的执行效率是不是会大幅度提升呢?
使用 async 并发
image-20210615150802387/*
使用async与await实现并发
从概念上来说,async就像是launch一样。它会开启一个单独的协程,这个协程是个轻量级线程,可以与其他协程并发工作。区别在于,launch
会返回一个Job,但是Job并不会持有任何结果值,而async会返回一个Deferred,这是一个轻量级的非阻塞的future,它代表一个promise,可以
在稍后提供一个结果值。
可以通过在一个deferred值上调用.await()方法来获取最终的结果值,Deferred也是个Job,因此可以在需要时对其进行取消
*/
fun main() = runBlocking {
val elapsedTime = measureTimeMillis {
val value1 = async { intValue1() }
val value2 = async { intValue2() }
val result1 = value1.await()
val result2 = value2.await()
println("$result1 + $result2 = ${result1 + result2}")
}
println("total time: $elapsedTime")
}
private suspend fun intValue1(): Int {
delay(2000)
return 15
}
private suspend fun intValue2(): Int {
delay(1000)
return 20
}
RUN> 🏄🏄🏄🏄🏄🏄
15 + 20 = 35 total time: 2021 Process finished with exit code 0
总耗时2秒多,说明程序是并行执行的
image-20210615151457111
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
image-20210615151137938
Deferred就是一个Job
image-20210615151233515惰性启动的 async
image-20210615151610474async是接收三个参数的,另外两个参数有默认值:
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
image-20210615151755225
Default -- immediately schedules the coroutine for execution according to its context.
根据上下文会立即调用协程执行
也就是上一次咱们做的这个async中的代码块是立即会执行的,不是延时执行的,但是!!在实际场景中可以会有遇到async时不希望立刻就来执行,而是这个执行的时机由程序员在某个条件下来触发,此时这个CoroutineStart参数就需要改变一下不能用默认参数了,需要用它了:LAZY
/**
* Starts the coroutine lazily, only when it is needed.
*
* See the documentation for the corresponding coroutine builders for details
* (like [launch][CoroutineScope.launch] and [async][CoroutineScope.async]).
*
* If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
* execution at all, but will complete with an exception.
*/
LAZY,
接下来就是要来启动延迟的async进行执行了
/*
关于async的延迟执行
我们可以通过将async方法的start参数设置为CoroutineStart.LAZY来实现协程的延迟执行。
在这种情况下,协程会在两种场景下去执行:调用Deferred的await方法,或是调用Job的start方法。
*/
fun main() = runBlocking {
val elapsedTime = measureTimeMillis {
val value1 = async(start = CoroutineStart.LAZY) { intValue1() }
val value2 = async(start = CoroutineStart.LAZY) { intValue2() }
println("hello world")
// Thread.sleep(3000)
delay(3000L)
// 尝试注释掉如下两行代码
value1.start()
value2.start()
val result1 = value1.await()
val result2 = value2.await()
println("$result1 + $result2 = ${result1 + result2}")
}
println("total time: $elapsedTime")
}
private suspend fun intValue1(): Int {
delay(2000)
return 15
}
private suspend fun intValue2(): Int {
delay(1000)
return 20
}
image-20210615152334555RUN> 🏂🏂🏂🏂🏂🏂
hello world 15 + 20 = 35 total time: 5046
delay(3000L)+intValue1(2000) = 5秒,程序是并行执行的
fun main() = runBlocking {
val elapsedTime = measureTimeMillis {
val value1 = async(start = CoroutineStart.LAZY) { intValue1() }
val value2 = async(start = CoroutineStart.LAZY) { intValue2() }
println("hello world")
// Thread.sleep(3000)
delay(3000L)
// 尝试注释掉如下两行代码
// value1.start()
// value2.start()
val result1 = value1.await()
val result2 = value2.await()
println("$result1 + $result2 = ${result1 + result2}")
}
println("total time: $elapsedTime")
}
private suspend fun intValue1(): Int {
delay(2000)
return 15
}
private suspend fun intValue2(): Int {
delay(1000)
return 20
}
RUN> 🏄🏄🏄🏄🏄🏄
hello world 15 + 20 = 35 total time: 6027 Process finished with exit code 0
delay(3000L)+intValue1(2000) +intValue2(1000) 程序串行执行
其实根源是:
image-20210615152635899
所以,如果将async变成了lazy了之后需要特别的注意!!!如果使用不当的时候跟不用async没任何区别了,这样就失去了async的使用意义了。
async ⻛格的函数
image-20210615152955681/*
异步风格的函数
*/
fun main() {
val elapsedTime = measureTimeMillis {
val value1 = intValue1Async()
val value2 = intValue2Async()
runBlocking {
println("the answer is: ${value1.await() + value2.await()}")
}
}
println("total time: $elapsedTime")
}
private suspend fun intValue1(): Int {
delay(2000)
return 15
}
private suspend fun intValue2(): Int {
delay(3000)
return 20
}
fun intValue1Async() = GlobalScope.async {
intValue1()
}
fun intValue2Async() = GlobalScope.async {
intValue2()
}
RUN> 🏄🏄🏄🏄🏄🏄
image-20210615153228945the answer is: 35 total time: 3161 Process finished with exit code 0
结构化并发程序开发:
/*
使用async进行结构化并发程序开发
*/
fun main() = runBlocking {
val elapsedTime = measureTimeMillis {
println("the answer is: ${intSum()}")
}
println("total time: $elapsedTime")
}
private suspend fun intSum(): Int = coroutineScope {
val value1 = async { intValue1() }
val value2 = async { intValue2() }
value1.await() + value2.await()
}
private suspend fun intValue1(): Int {
delay(2000)
return 15
}
private suspend fun intValue2(): Int {
delay(3000)
return 20
}
RUN> 🏂🏂🏂🏂🏂🏂
the answer is: 35 total time: 3019 Process finished with exit code 0
取消始终通过协程的层次结构来进行传递:
image-20210615153625963/*
关于父子协程的异常与取消问题
协程的取消总是会沿着协程层次体系向上进行传播
*/
fun main() = runBlocking<Unit> {
try {
failureComputation()
} finally {
println("Computation failed")
}
}
private suspend fun failureComputation(): Int = coroutineScope {
val value1 = async<Int> {
try {
delay(9000000)
50
} finally {
println("value1 was cancelled")
}
}
val value2 = async<Int> {
Thread.sleep(2000)
println("value2 throws an exception")
throw Exception()
}
value1.await() + value2.await()
}
RUN> 🏂🏂🏂🏂🏂🏂
image-20210615154029585value2 throws an exception value1 was cancelled Computation failed Exception in thread "main" java.lang.Exception at com.shengsiyuan.coroutines3.HelloKotlin6Kt$failureComputation$2$value2$1.invokeSuspend(HelloKotlin6.kt:37) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274) at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:84) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59) at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source) at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38) at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source) at com.shengsiyuan.coroutines3.HelloKotlin6Kt.main(HelloKotlin6.kt:14) at com.shengsiyuan.coroutines3.HelloKotlin6Kt.main(HelloKotlin6.kt) Process finished with exit code 1
“协程的取消总是会沿着协程层次体系向上进行传播。”,以上就是解决在async中如果出异常的一个比较好的解决方案。