前言
在使用协程的过程中,有几个不同的启动协程的函数,分别适用于不同的场景。主要包括withContext、launch、async,这篇文章主要是通过一个例子来看看这几个协程启动函数的区别,每一个函数都有其使用的场景,可以根据要实现的目标来选择到底应该使用哪个函数。
launch
lauch是最常见的启动一个协程的方法,可以通过GlobalScope.launch开启一个全局生命周期的协程,也可以通过CoroutineScope(CoroutineContext).launch 来开启一个在指定的 CoroutneContext 范围的协程。也可以记录这个Job并通过Job.cancel()随时取消。首先看下面的例子:
fun main(args: Array<String>) {
println("main")
testLaunch()
Thread.sleep(2000)
}
fun testLaunch() {
GlobalScope.launch {
func1()
}
GlobalScope.launch {
func2()
}
}
suspend fun func1() {
delay(1500)
println("func1")
}
suspend fun func2() {
delay(300)
println("func2")
}
在上面的例子中,func2执行时间要快于func1,通过启动两个协程来看一下两个函数的执行顺序。使用launch不会阻塞主线程,但是由于launch不是一个suspend函数,所以同时也不会等待协程的返回结果。
输出:
before
func2
func1
after
根据输出可以看到,launch并不会等待当前协程的执行结果,所以launch适用于一些不需要等待返回的函数,或者可以通过回调的方式,当获取到结果的时候执行一些操作。
withContext
接下来看看withContext,将上述例子换成withContext,来看看和使用launch会有什么不同:
fun testWithContext() {
GlobalScope.launch {
func1WithContext()
func2WithContext()
}
}
suspend fun func1WithContext() = withContext(Dispatchers.IO) {
delay(1500)
println("func1")
}
suspend fun func2WithContext() = withContext(Dispatchers.IO) {
delay(300)
println("func2")
}
输出:
before
func1
func2
after
通过输出可以看到,func1WithContext和func2WithContext是顺序执行的。由于withContext是suspend函数,所以协程执行到这里会挂起直到该函数完成。withContext需要传入一个调度器的参数,指定协程运行在哪个线程。
可以看出来,当需要等待函数的执行结果时可以使用withContext,withContext会等待结果返回并且不会阻塞主线程。但这里有一个问题,假如在同一个协程内,有多个耗时任务,但是相互之前并没有逻辑关系,这时使用withContext使得所有任务串行是低效的。那么有没有一种方式是可以并行执行多个任务,但又会等待所有任务的返回结果呢?来看看下面的async。
async:
我们将例子修改为使用async看看执行结果,简单修改一下函数,两个函数分别返回一个值,最后来计算两个函数返回值的和:
fun testAsync() {
GlobalScope.launch {
val deferred1 = async(Dispatchers.IO) { func1Async() }
val deferred2 = async(Dispatchers.IO){ func2Async() }
val result = deferred1.await() + deferred2.await()
println("the sum is: $result" )
}
}
suspend fun func1Async(): Int {
delay(1500)
println("func1")
return 1
}
suspend fun func2Async(): Int {
delay(300)
println("func2")
return 2
}
输出:
before
func2
func1
the sum is: 3
after
通过上面的输出可以看到,func2Async比func1Async更快执行,说明两个函数是并行的。但是最后的结果是正确的,这就是async的作用。可**以看到async返回的是Deferred<T>。Deferred<T>表示一个非阻塞的课取消的值,继承自Job。**看例子中最后使用到了deferred1.await() + deferred2.await(),就表示需要等待两个async的值都返回才会执行,**其中await()其实就是一个suspend函数。**当不使用await()的时候,async和launch 的效果是一样的。还有一点需要注意的是await的调用时机,如果把上述例子改为:
val deferred1 = async(Dispatchers.IO) { func1Async() }.await()
val deferred2 = async(Dispatchers.IO){ func2Async() }.await()
val result = deferred1 + deferred2
如果这样调用作用就与withContext一样了。协程会阻塞直到func1Async()执行完成才会执行func2Async()。
可以看到,async适用于多个并行任务但又需要等待结果的情况。
总结
- launch
- 不会阻塞直到结果返回
- 不会阻塞线程
- 并行执行
- withContext:
- 会阻塞当前协程直到函数返回
- 从指定的Dispatcher执行函数
- 当执行函数的时候不会阻塞线程
- 串行执行
- async
- 当使用
awiat函数时,会阻塞直到结果返回 - 如果不使用await,其效果与launch一样
- 适用于多个并行任务但需要等待结果返回情形
- 并行执行
- 当使用
顺便打个广告,猿辅导研发招聘各岗位,有大量HC。感兴趣的话可以简历发我的邮箱哦:fb0122@163.com。欢迎各位大佬来撩~ 😜 😜