先来看下官网的表述:
In addition to the coroutine scope provided by different builders, it is possible to declare your own scope using coroutineScope builder. It creates a coroutine scope and does not complete until all launched children complete. The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a coroutine scope
launch {
delay(500L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before the nested launch
}
println("Coroutine scope is over") // This line is not printed until the nested launch completes
}
上面的这段描述很简单,但是,执行完上面的代码后,就有点小迷惑了,按照官方的说法,coroutineScope
是不会阻塞当前线程的,那么Coroutine scope is over
应该在coroutineScope
代码段执行之前打印才对啊,为什么每次执行的结果都是最后才打印Coroutine scope is over
?
if (你清楚的直到其中的原理) {
return
}
其实,我看完之后,产生迷惑的原因主要是因为官方拿runBlocking
和coroutineScope
来比较,给出的例子也不直观。但是其实他们俩完全是不同的东西。让我们自己重新换个例子再来理解下:
1. runBlocking
Runs a new coroutine and blocks the current thread interruptibly until its completion
fun main() {
runBlocking {
Log.i("CoroutineDemo", "before delay in coroutine")
delay(500L)
Log.i("CoroutineDemo", "after delay in coroutine")
}
Log.i("CoroutineDemo", "out coroutine")
}
这里用runBlocking创建来一个协程并立即执行,按照runBlocking签名上的注释来说,runBlocking会阻塞当前线程,所以,我们在协程里用delay来挂起协程,看看打印结果:
CoroutineDemo: before delay in coroutine
CoroutineDemo: after delay in coroutine
CoroutineDemo: out coroutine
很显然,runBlocking确实阻塞了当前线程。
2. coroutineScope
Creates a [CoroutineScope] and calls the specified suspend block with this scope.
coroutineScope只是一个suspend function,它和runBlocing有本质的区别。
fun main() {
GlobalScope.launch(start = CoroutineStart.UNDISPATCHED) {
coroutineScope {
Log.i("CoroutineDemo", "before delay in coroutine")
delay(500L)
Log.i("CoroutineDemo", "after delay in coroutine")
}
}
Log.i("CoroutineDemo", "out coroutine")
}
这里我们用GlobalScope来创建一个顶级的协程,并设置start参数为UNDISPATCHED,立即执行。
来直接看执行结果:
CoroutineDemo: before delay in coroutine
CoroutineDemo: out coroutine
CoroutineDemo: after delay in coroutine
很显然,coroutineScope在执行完delay挂起之前的代码后,就立即将控制权交给了所在的线程,线程并没有被阻塞,并立即去执行协程外面的事情去了。最后挂起时间到了后,才又将控制权交回协程,继续执行delay后面的代码。
由上面两个例子可见,runBlocking和coroutineScope除了有本质的区别外,最大的不同就在于两者所在的协程被挂起后对所在线程的影响,runBlocking所在协程被挂起后会阻塞所在线程,线程不能处理协程之外的事情;coroutineScope所在的协程被挂起后,则会立即交出控制权给所在的线程,不会阻塞线程,线程可以处理协程之外的事情。
相关的解释请参考:
Coroutines: runBlocking vs coroutineScope
最后帮朋友打个小广告