协程基础
- 协程就是一个线程框架;
- 本质上,协程是轻量级的线程
第一个协程程序
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
println("World!") // 在延迟后打印输出
}
println("Hello,") // 协程已在等待时主线程还在继续
Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}
代码的运行结果:
Hello,
World!
- 我们使用
GlobalScope.launch在GlobalScope中启动了协程,这意味着新协程的生命周期只受整个应用的生命周期的限制 delay(long timeMill)是一个特殊的挂起函数,它不会造成线程阻塞,但是会挂起协程,并且只能在协程中使用
结构化的并发
当我们使用GlobalScope.launch时,我们会创建一个顶层协程。虽然它很轻量,但它运行时仍会消耗一些内存资源。如果我们忘记保持对新启动的协程的引用,它还会继续运行。如果协程中的代码挂起了会怎么样(例如,我们错误地延迟了太⻓时间),如果我们启动了太多的协程并导致内存不足会怎么样?必须手动保持对所有已启动协程的引用并join之很容易出错。
有个更好的解决办法,我们可以在执行操作所在的指定作用域内启动协程,而不是像通常使用线程(线程总是全局的)那样在GlobalScope中启动
我们使用runBlocking协程构建器将main函数转换为协程。包括runBlocking 在内的每个协程构建器都将CoroutineScope的实例添加到其代码块所在的作用域中。我们可以在这个作用域中启动协程而无需显式join之,因为外部协程(示例中的 runBlocking)直到在其作用域中启动的所有协程都执行完毕后才会结束。因此,可以将我们的示例简化为:
import kotlinx.coroutines.*
fun main() = runBlocking { // this:
CoroutineScope launch { // 在 runBlocking 作用域中启动一个新协程
delay(1000L)
println("World!")
}
println("Hello,")
}
作用域构建器
-
除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一个协程作用域并且在所有已启动子协程执行完毕之前不会结束。
-
runBlocking 与 coroutineScope
可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。这两者的主要区别在于,runBlocking 方法会阻塞当前线程来等待,而 coroutineScope只是挂起,会释放底层线程用于其他用途。由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数
可以通过以下示例来演示:
fun main()= runBlocking{
launch {
delay(200L)
println("Task from runBlocking") }
coroutineScope {
launch {
delay(500L)
println("Task from nested launch") }
delay(100L)
println("Task from coroutine scope")
}
println("Coroutine scope is over")
}
运行结果:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
全局协程像守护线程
示例:
import kotlinx.coroutines.*
fun main() = runBlocking {
GlobalScope.launch {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // just quit after delay
}
运行结果:
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
Process finished with exit code 0
在GlobalScope中启动的活动协程并不会使进程保活。