Kotlin协程的挂起函数编写(六)

一、前言

在之前的例子中,我们知道可以通过launch或者async来启动协程,并可以控制其生命周期,而且还知道了通过async的异步可以做到并行运行。这里记录一下其它的操作

二、延迟启动协程

协程可以通过提前定义,然后当某一条件触发时候再进行启动,如下

private suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // pretend we are doing something useful here
    return 13
}

private suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // pretend we are doing something useful here, too
    return 29
}
@Test
fun lazy(){
    runBlocking {
        val time = measureTimeMillis {
            val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
            val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
            // some computation
            one.start() // start the first one
            two.start() // start the second one
            println("The answer is ${one.await() + two.await()}")
        }
        println("Completed in $time ms")
    }
}

对于延时启动的函数,可以使用Job.start()或者Deffered.await()来进行启动,不过这里的话先使用Job.start()启动再使用Deffered.await()来获取结果,因为这样才不会导致并行的程序因为await()变成串行程序

三、GlobalScope.async

其实官方是不建议直接使用GlobalScope启动协程的因为不好控制。例如我们编写一个异步的任务(异步任务通常使用"…Async"的方式命名)。

  • GlobalScope是一个微妙的 API,可能会以非平凡的方式适得其反,下面将解释其中一种方式,因此您必须明确选择使用@OptIn(DelicateCoroutinesApi::class)
// The result type of somethingUsefulOneAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

// The result type of somethingUsefulTwoAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
    doSomethingUsefulTwo()
}

通过GlobalScope.async启动的代码可以在任何地方执行异步操作,如下

// The result type of somethingUsefulOneAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

// The result type of somethingUsefulTwoAsync is Deferred<Int>
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
    doSomethingUsefulTwo()
}
@Test
fun taskAsync(){
    runBlocking {
        // note that we don't have `runBlocking` to the right of `main` in this example
            val time = measureTimeMillis {
                // we can initiate async actions outside of a coroutine
                val one = somethingUsefulOneAsync()
                val two = somethingUsefulTwoAsync()
                // but waiting for a result must involve either suspending or blocking.
                // here we use `runBlocking { ... }` to block the main thread while waiting for the result
                runBlocking {
                    println("The answer is ${one.await() + two.await()}")
                }
            }
            println("Completed in $time ms")
    }
}

这个代码本身没有问题,倘若出于某种原因协程要取消掉,比如出现异常,在val one = somethingUsefulOneAsync()one.await()中间出现错误,那么即使明确调用了cancel()somethingUsefulOneAsync()还是会在后台默默运行。这里可以使用coroutineScope来解决这个问题。所以需要修改为以下方式

suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}
@Test
fun taskAsync(){
    runBlocking {
      val time = measureTimeMillis {
          println("The answer is ${concurrentSum()}")
      }
      println("Completed in $time ms")
    }
}

这是因为coroutineScope函数是个挂起函数在调用的地方需要跟使用它的作用域绑定,这样当作用域取消时,这个函数的内容也取消了。

四、参考链接

  1. 编写挂起函数
    https://kotlinlang.org/docs/composing-suspending-functions.html#lazily-started-async
  2. 如何使用 kotlin 协程并行运行两个作业但等待另一个作业完成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值