一、前言
在之前的例子中,我们知道可以通过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
函数是个挂起函数在调用的地方需要跟使用它的作用域绑定,这样当作用域取消时,这个函数的内容也取消了。