基础
本质上,协程是轻量级的线程。
runBlocking
用于在当前线程中阻塞执行协程代码直到协程执行完毕。它通常用于在主函数或测试代码中使用,以确保协程代码的顺序执行。
GlobalScope.launch
是一个*函数,用于在全局范围内启动一个新的协程。它创建一个*协程,该协程的生命周期与应用程序的生命周期相同。
delay
是协程库中的一个挂起函数,用于延迟指定的时间。它会不会阻塞整个线程,只会暂停当前协程的执行,并在指定的时间过后恢复执行。
桥接阻塞与非阻塞的世界
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
println("World!") // 在延迟后打印输出
}
println("Hello,") // 协程已在等待时主线程还在继续
runBlocking { // 但是这个表达式阻塞了主线程
delay(2000L) // ……我们延迟 2 秒来保证 JVM 的存活
}
}
//输出:
//Hello,
//World!
等待一个作业
问题:显式阻塞比较Hack;
解决方案:使用join()去实现非阻塞方式等待所启动的后台 Job 执行结束。
//显式(以非阻塞方式)等待所启动的后台 Job 执行结束:
fun main() = runBlocking {
val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // 等待直到子协程执行结束
}
//输出:
//Hello,
//World!
结构化并发
问题:GlobalScope.launch
会创建一个顶层协程。虽然它很轻量,但它运行时仍会消耗一些内存资源。可能会造成:内存不足、延迟太久等各种问题。
解决方案:在执行操作所在的指定作用域内启动协程, 而不是像通常使用线程(线程总是全局的)那样在 GlobalScope 中启动。
//显式(以非阻塞方式)等待所启动的后台 Job 执行结束。
//在这个作用域中启动协程而无需显式 join 之,因为外部协程(示例中的 runBlocking)直到在其作用域中启动的所有协程都执行完毕后才会结束。
fun main() = runBlocking {// this: CoroutineScope
launch { // 在 runBlocking 作用域中启动一个新协程
delay(1000L)
println("World!")
}
println("Hello,")
}
coroutineScope
和 runBlocking
都会阻塞当前线程,但它们之间的主要区别是:runBlocking 方法会阻塞当前线程来等待, 而