协程的定义: 本质上,协程是轻量级的线程(线程的框架(API))
1.引入协程包
implementation ‘org.jetbrains.kotlinx:kotlinx-corotines-core:1.4.2’
使用时引入 import kotlinx.coroutines.*
2.启动一个协程
fun main(){
//后台运行一个新的协程
GlobalScope.launch{
//delay是一个特殊的挂起函数,它不会造成线程阻塞,但是会挂起协程,并且只能在协程中使用
delay(1000)
println("World!")
}
println("Hello,")
//协程还在等待时主线程还在继续,阻塞主线程2秒钟来保证JVM存活
Thread.sleep(2000)
}
执行结果:
Hello,
World!
作为用户感觉上,阻塞和挂起是一样的。
- 挂起:一般是主动的,由系统或程序发出,甚至于辅存中去。(不释放cpu,可能释放内存,放在外存)
- 阻塞:一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待某种资源或信号量(即有了资源)将它唤醒。(释放cpu,不释放内存)
例子:
- 挂起,是车子把货卸了,但是车还在开,车有可能又在拖其他货物了
- 阻塞,是货没卸,车子停了,在等红绿灯,变绿灯了就走
3.runBlocking
fun main(){
//后台运行一个新的协程
GlobalScope.launch{
delay(1000)
println("World!")
}
println("Hello,")
//这个表达式阻塞了主线程,我们延迟2秒来保证JVM的存活
runBlocking{
delay(2000)
}
}
- 结果是相似的,但是我们使用了非阻塞的函数delay
- 调用了runBlocking的主线程会一直阻塞直到runBlocking内部协程执行完毕
接下来我们我们使用runBlocking来包装main函数的执行:
fun main() = runBlocking<Unit>{//开始执行主协程
GlobalScope.launch{//在后台启用一个新的协程并继续
delay(1000)
println("World!")
}
println("Hello,")//主协程在这里会立即执行
delay(1000)//延迟2秒保证JVM执行
}
4.join (等待所启动的后台Job执行结束)
fun main() = runBlocking<Unit>{//开始执行主协程
val job = GlobalScope.launch{
delay(1000)
println("World!")
}
println("Hello,")
job.join()//等待直到子协程结束
//.....
}
简化写法,我们可以在执行操作的所在作用域内启动协程,而不是像通用使用线程(线程总是全局的)那样在GlobalScope中启动
fun main() = runBlocking<Unit>{//开始执行主协程
launch{
delay(1000)
println("World!")
}
println("Hello,")
//.....
}
在上面实例中,我们使用runBlocking协程构建器将main函数转换为协程。
包括runBlocking在内的协程构建器都将CoroutineScope的实例添加到其代码块所在的作用域中。
我们可以在这个作用域中启动协程而无需使用join,直到在其作用域中启动的所有协程都执行完毕之后才会结束
5.作用域构建器CoroutineScope
- 除了由不同的构建起构建器提供协程作用域外,还可以使用coroutineScope构建器声明自己的作用域
- 它会创建一个协程作用域并且在所有已启动子协程执行完毕之前不会结束。
代码:
fun main() = runBlocking<Unit>{
launch{
delay(200L)
println("Task from runBlocking")
}
//创建一个协程作用域
coroutineScope {
launch{
delay(500L)
println("Task from nested launch")
}
delay(100L)
println("Task from corount scope")
}
println("Coroutine scope is over")
}
执行结果:
Task from corount scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
- runBlocking与CoroutineScope看起来很相似,因为他们都会等待协程体以及所有子协程结束。
- 主要区别在于,runBlocking方法会阻塞当前线程来等待,而coroutineScope只是挂起,会释放底层线程用于其他用途。
- 由于存在这点差异,runBlocking是常规函数,coroutineScope是挂起函数。