美文网首页Kotlin
Kotlin-通过async与await实现高效并发

Kotlin-通过async与await实现高效并发

作者: 蒋斌文 | 来源:发表于2021-06-15 15:48 被阅读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-20210615151610474

    async是接收三个参数的,另外两个参数有默认值:

    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
    }
    

    RUN> 🏂🏂🏂🏂🏂🏂

    hello world
    15 + 20 = 35
    total time: 5046
    

    delay(3000L)+intValue1(2000) = 5秒,程序是并行执行的

    image-20210615152334555
    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> 🏄🏄🏄🏄🏄🏄

    the answer is: 35
    total time: 3161
    
    Process finished with exit code 0
    
    image-20210615153228945

    结构化并发程序开发:

    /*
        使用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> 🏂🏂🏂🏂🏂🏂

    value2 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
    
    image-20210615154029585

    协程的取消总是会沿着协程层次体系向上进行传播。”,以上就是解决在async中如果出异常的一个比较好的解决方案。

    相关文章

      网友评论

        本文标题:Kotlin-通过async与await实现高效并发

        本文链接:https://www.haomeiwen.com/subject/okvzeltx.html