前言
在使用协程的过程中,有几个不同的启动协程的函数,分别适用于不同的场景。主要包括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。欢迎各位大佬来撩~ 😜 😜