-
引例:
private suspend fun intValue1(): Int { delay(1000) return 1 } private suspend fun intValue2(): Int { delay(2000) return 2 } fun main() = runBlocking { val elapsedTime = measureTimeMillis { val value1 = intValue1() val value2 = intValue2() println("the result is ${value1 + value2}") } println("the elapsedTime is $elapsedTime") }
先介绍一下measureTimeMillis{}:
/** * Executes the given [block] and returns elapsed time in milliseconds. */ public inline fun measureTimeMillis(block: () -> Unit): Long { val start = System.currentTimeMillis() block() return System.currentTimeMillis() - start }
用来计算代码的执行时间的简便方法。然后来看输出结果:
the result is 3
the elapsedTime is 3018这个例子想要说明什么问题呢?要想实现功能A,需要依赖于功能B和功能C的结果,如果功能B和功能C之间又有依赖关系,那么只能B和C顺序执行;但是如果B和C是完全独立的,那么B和C就可以同时进行,从而缩短实现功能A所需时间。就好像本例中的intValue1()和intValue2(),它们是完全独立的,但是从运行结果来看,耗费的时间是intValue1()的时间 + intValue2()的时间。那么怎么实现协程的高效并发呢?
-
使用async和await改善代码:
fun main() = runBlocking { val elapsedTime = measureTimeMillis { val value1 = async { intValue1() } val value2 = async { intValue2() } println("the result is ${value1.await() + value2.await()}") } println("the elapsedTime is $elapsedTime") } private suspend fun intValue1(): Int { delay(1000) return 1 } private suspend fun intValue2(): Int { delay(2000) return 2 }
输出结果为:
the result is 3
the elapsedTime is 2016从结果上看,所耗时间变短了,执行效率也就提高了。
-
async:
public fun <T> CoroutineScope.async( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> T ): Deferred<T>
Creates a coroutine and returns its future result as an implementation of [Deferred]. The running coroutine is cancelled when the resulting deferred is [cancelled][Job.cancel].
通过aysnc的方法签名以及它的文档注释,我们可以得知:
- 它和CoroutineScope.launch{}类似,可以用来创建一个协程,不同的是launch的返回结果是Job类型,而aysnc的返回结果是Deferred类型
- 结果Deferred和Job一样,也能够被取消。
至此,我们创建协程的方式就有了五种:
- GlobalScope.launch{}
- launch{}
- runBlocking{}
- coroutineScope{}
- async{}
那么Deferred到底是个什么东东?它和Job之间又有怎样的联系和区别?
public interface Deferred<out T> : Job
Deferred value is a non-blocking cancellable future — it is a [Job] with a result
从类的层次结构上看,Deferred是Job的子接口;从功能上来看,Deferred就是带返回结果的Job。
-
await: Deferred接口定义的方法
Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,returning the resulting value or throwing the corresponding exception if the deferred was cancelled
- 不会阻塞当前线程
- 会等待,当计算完毕时,恢复执行
- 会返回结果值或者由于被取消而对应的异常
-
CoroutineScope.async()中的start参数:默认值为CoroutineStart.DEFAULT
/** * Defines start options for coroutines builders. * It is used in `start` parameter of [launch][CoroutineScope.launch], [async][CoroutineScope.async], and other coroutine builder functions. * * The summary of coroutine start options is: * * [DEFAULT] -- immediately schedules coroutine for execution according to its context; * * [LAZY] -- starts coroutine lazily, only when it is needed; * * [ATOMIC] -- atomically (in a non-cancellable way) schedules coroutine for execution according to its context; * * [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_. */ public enum class CoroutineStart { DEFAULT, LAZY, ATOMIC, UNDISPATCHED }
- DEFAULT: 默认值,它会上下文立即调度线程的执行
- LAZY:它不会立即调度协程的执行,而是在需要的时候才会触发执行
- ATOMIC:原子性调度,即不会被取消
- UNDISPATCHED:也会立即调度,直到当前的第一个挂起点,这个后面讨论分发器的时候还会说
这个参数在launch里面也有,表达的含义是一致的。
我们现在试一下LAZY的使用:
fun main() = runBlocking { val elapsedTime = measureTimeMillis { val intValue1 = async(start = CoroutineStart.LAZY) { intValue1() } val intValue2 = async(start = CoroutineStart.LAZY) { intValue2() } println("hello world") val result1 = intValue1.await() val result2 = intValue2.await() println("the result is : ${result1 + result2}") } println("elapsedTime = $elapsedTime") } private suspend fun intValue1(): Int { delay(1000) return 15 } private suspend fun intValue2(): Int { delay(2000) return 20 }
hello world
the result is : 35
elapsedTime = 3018一旦添加了这个参数之后,我们会发现,async的并发失效了。结合前面的阐述,我们不难得出结论:由于CoroutineStart.LAZY的作用,我们async启动的两个协程并没有立即执行。而是直到调用await方法之后,才开始执行,而await又是会去等待结果,自然需要等待intValue1协程执行完毕后,遇到intValue2.await(),才会触发intValue2协程的执行,又要去等待。那么,async的并发也就失效了。
fun main() = runBlocking { val elapsedTime = measureTimeMillis { val intValue1 = async(start = CoroutineStart.LAZY) { intValue1() } val intValue2 = async(start = CoroutineStart.LAZY) { intValue2() } println("hello world") intValue1.start() intValue2.start() val result1 = intValue1.await() val result2 = intValue2.await() println("the result is : ${result1 + result2}") } println("elapsedTime = $elapsedTime") } private suspend fun intValue1(): Int { delay(1000) return 15 } private suspend fun intValue2(): Int { delay(2000) return 20 }
我们可以通过Deferred的start方法,手动触发协程的执行,输出结果如下:
hello world
the result is : 35
elapsedTime = 2013换句话说,使用了CoroutineStart.LAZY参数之后,协程不会立马执行,直到调用了start()或await()才会触发协程的调度。