public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont, true)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
它是一个suspend
函数,创建一个新的协程作用域,并在该作用域内执行指定代码块,它并不启动协程。其存在的目的是进行符合结构化并发的并行分解(即,将长耗时任务拆分为并发的多个短耗时任务,并等待所有并发任务完成后再返回)。
coroutineScope
与runBlocking
的区别在于runBlocking
会阻塞当前线程,而coroutineScope
会挂起所在的协程直至其内部任务(包括子协程)执行完成,它不会阻塞所在的线程。
coroutineScope
是一个挂起函数,它被挂起后,会转而执行之前的子协程。
fun main() = runBlocking {
launch { //launch①
delay(1000) //挂起launch①
println("test2")
}
println("test1")
coroutineScope { //第一次挂起runBlocking,直至内部逻辑完成
launch { //launch②
delay(2000) //挂起launch②
println("test3")
}
delay(5000) //delay① //第二次挂起runBlocking
println("test4")
}
println("test5")
}
//test1
//test2
//test3
//test4
//test5
上述代码分析:
-
runBlocking
在main
线程创建并启动一个阻塞的协程; - 创建
launch①
子协程,由于创建协程是需要一些时间的,并且协程的创建是由特定的线程来完成,并非是main线程。所以在创建协程过程中会并行地执行后续代码。因此test1
被输出。 - 执行到
coroutineScope
函数时,把runBlocking
挂起,直到内部逻辑执行完成。 - 然后创建
launch②
协程,创建过程中执行执行后续代码:delay①
继续挂起runBlocking
5s(挂起函数中调用挂起函数)。 - 等到
launch①
创建完毕时,把它挂起1s。launch②
创建完毕时,把它挂起2s。 - 此时
runBlocking、launch①、launch②
都是被挂起状态。 - 等到1s后
launch①
恢复,输出test2
;2s后launch②
被恢复,输出test3
;5s后runBlocking
第二次挂起被恢复,输出test4
。 - 此时
coroutineScope
中的逻辑已经执行完成,恢复runBlocking
的第一次挂起,test5
被输出。
这比较难以理解,下面的案例稍微容易些:
fun main() = runBlocking {
launch {
println("test3")
}
println("test1")
coroutineScope { //挂起runBlocking,直到内部逻辑完成
println("test2")
delay(1000) //挂起runBlocking5s
println("test4")
}
println("test5") //必须等待挂起函数coroutineScope执行完毕后才会被执行
}
//test1
//test2
//test3
//test4
//test5
而如果把coroutineScope
函数改成delay
函数,会更加容易理解,因为它们都是挂起函数。
fun main() = runBlocking {
launch {
delay(1000)
println("test2")
}
println("test1")
delay(2000) //挂起runBlocking协程2s
println("test3")
}
//test1
//test2
//test3
coroutineScope
经常用来把一个长耗时的任务拆分成多个子任务,使这些子任务并行执行
suspend fun showSomeData() = coroutineScope {
val data1 = async { //子任务1
delay(2000)
100
}
val data2 = async { //子任务2
delay(3000)
20
}
withContext(Dispatchers.Default) { //合并结果并返回
delay(3000)
val random = Random(10)
data1.await() + data2.await() + random.nextInt(100)
}
}
coroutineScope
有如下语义:
-
并行执行内部任务
data1
、data2
、withContext
-
如果其它任务(
random
)抛出异常,data1
和data2
两个任务会被取消 -
如果
showSomeData()
被取消,内部的data1
、data2
、withContext
都会被取消 -
如果
data1
、data2
失败,withContext
被取消。
网友评论