在理解“协程在处理挂起的时候,不会阻塞当前线程”这句话的时候,确实感觉很懵逼,于是想着用demo来证明一下,话不多说,实验如下
三个实验,只调换两个子协程的顺序
实验一
MainScope().launch(Dispatchers.Main) {
Log.d("zs", "6")
val s = suspendCoroutineTest()
Log.d("zs", "s = "+s)
delay(3000)
Log.d("zs", "do others")
launch {
Log.d("zs", "enter3 launch")
delay(3000)
Log.d("zs", "enter1")
}
launch {
Log.d("zs", "enter2 launch")
delay(5000)
Log.d("zs", "enter2")
}
}
输出:
实验二、
MainScope().launch(Dispatchers.Main) {
launch {
Log.d("zs", "enter2 launch")
delay(5000)
Log.d("zs", "enter2")
}
Log.d("zs", "6")
val s = suspendCoroutineTest()
Log.d("zs", "s = "+s)
delay(3000)
Log.d("zs", "do others")
launch {
Log.d("zs", "enter3 launch")
delay(3000)
Log.d("zs", "enter1")
}
}
输出:
实验三
MainScope().launch(Dispatchers.Main) {
launch {
Log.d("zs", "enter2 launch")
delay(5000)
Log.d("zs", "enter2")
}
launch {
Log.d("zs", "enter3 launch")
delay(3000)
Log.d("zs", "enter1")
}
Log.d("zs", "6")
val s = suspendCoroutineTest()
Log.d("zs", "s = "+s)
delay(3000)
Log.d("zs", "do others")
}
输出
分析:可以看到,仅仅替换两个子协程的顺序关系,三个实验运行的总时长均不相同
实验一中两个子协程均在父协程下面,所以父协程的挂起操作,就是“硬挂起”,也就是阻塞了当前线程,个人猜测是因为,两个子协程都还没有声明,线程就“假装不知道还有活要干”,就硬等了3s,然后去执行下面两个线程,在第一个线程挂起之后,就去执行了第二个,所以两个子携程总共耗时5s,因此累计耗时 3+5=8s
实验二中第一个子协程因为在父协程前面,可以理解为被声明过,所以在父协程挂起的时候,转身就去执行第一个子协程,然后碰到第一个子协程也挂起了,此时线程又没有活干了,于是等到父协程挂起结束,这里花了3s,此时第一个协程还在挂起中(还差2s),于是往下执行,碰到了第二个协程,也挂起了(第二个需要3s),所以过了2s,第一个协程挂起结束,此时第二个协程只剩1s就结束了,所以总耗时 3+2+1=6s
实验三中两个协程都相当于声明过了,等父协程挂起时,两个子协程都会触发执行,所以父协程和两个子协程的相当于是一同挂起的,过了3s后,父协程和第二个子协程都结束了,此时第一个还差2s,所以最终一共耗时3+2=5s
PS:这是笔者根据实验的结果猜想,并未探究源码,仅供理解参考