第一个协程
根据官方文档 可以了解到 以下例子:
fun main() {
GlobleScope.launch {// 启动一个协程 并继续执行后续代码,相当于守护线程
println("第一个协程!")
}
println("主线程执行结束!")
}
上面的例子 运行后 :
- 为何直接打印“主线程执行结束”而没有执行协程里面的打印呢?
协程的执行并不影响其它后续代码的执行,也就是说后面代码执行完,主线程就结束了,因此launch也随之消亡 - 怎样让协程先执行完,再执行后面的代码呢?
可以在最后面添加非阻塞或者阻塞延迟 以此来让协程在主线程结束之前有足够的执行时间,代码如下:
fun main() {
val job= GlobleScope.launch {// 启动一个协程 并继续执行后续代码
println("第一个协程!")
}
//job,cancel()// 协程取消
//job.join()// 协程挂起 直到此任务执行完成 此调用才可以恢复正常
job.cancelAndJoin()// 结合体
println("主线程执行结束!")
}
或者
fun main() {
GlobleScope.launch {// 启动一个协程 并继续执行后续代码
println("第一个协程!")
}
runBlocking {// 主协程
// 非阻塞延迟
delay(1000L)// 是挂起函数 只能应用于协程或者其它挂起函数中执行
}
println("主线程执行结束!")
}
或者
fun main() = runBlocking {
GlobleScope.launch {// 启动一个协程 并继续执行后续代码
println("第一个协程!")
}
// 非阻塞延迟
delay(1000L)// 是挂起函数 只能应用于协程或者其它挂起函数中执行
println("主线程执行结束!")
}
或者
suspend fun main() {// suspend 修饰的函数是挂起函数
GlobleScope.launch {// 启动一个协程 并继续执行后续代码
println("第一个协程!")
}
// 非阻塞延迟
delay(1000L)// 是挂起函数 只能应用于协程或者其它挂起函数中执行
println("主线程执行结束!")
}
或者
fun main() {
GlobleScope.launch {// 启动一个协程 并继续执行后续代码
println("第一个协程!")
}
// 阻塞延迟 等上面的执行1秒 再执行下面的代码
Thread.sleep(1000L)
println("主线程执行结束!")
}
或者 使用常用的thread方式
fun main() {
thread {
println("第一个协程!")
}
Thread.sleep(500L)
println("主线程执行完毕!")
}
注意点:挂起函数 只能作用于协程和其它挂起函数中
fun main() {
thread {
println("第一个协程!")
delay(1000L)// 报错 提示Suspension functions can be called only within coroutine body
}
println("主线程执行完毕!")
}
阻塞和非阻塞
- 阻塞:Thread.sleep(1000L) 阻碍后续代码的执行 等待1秒后 才会继续执行后续代码
- 非阻塞:delay(1000L) 不影响后续代码的执行 先执行后续的代码 1秒后就会把之前延迟1秒的协程再添加到可调度的队列中
// 主协程
fun main = runBlocking {
val job = GlobalScope.launch {
delay(500L)
println("第一个协程执行!")
}
delay(600L)
}
或者
suspend fun main {
val job = GlobalScope.launch {
delay(500L)
println("第一个协程执行!")
}
delay(600L)
}
或者
fun main {
val job = GlobalScope.launch {
delay(500L)
println("第一个协程执行!")
}
runBlocking {// 主线程
delay(600L)
}
}
等待工作
我们知道延迟可不是处理问题好的方式 上述的一些例子 都是通过等待来实现协程的执行然而我们有更好的方式来进行优化 那就是显示(非阻塞)join() 等待协程执行完成
fun main() {
val job = GlobalScope.launch {
println("第一个协程执行!")
}
runBlocking {
job.cancel()
job.join()// 挂起函数 只能运行在协程或者其它挂起函数中
}
println("结束语")
}
或者
fun main() = runBlocking {
val job = GlobalScope.launch {
println("第一个协程执行!")
}
// job.cancel()
// job.join()// 挂起函数 只能运行在协程或者其它挂起函数中
println("结束语")
}
或者
suspend fun main() {
val job = GlobalScope.launch {
println("第一个协程执行!")
}
job.cancel()
job.join()// 挂起函数 只能运行在协程或者其它挂起函数中
println("结束语")
}
结构化并发
fun main() = runBlocking { // this: CoroutineScope
launch { // launch a new coroutine in the scope of runBlocking
delay(1000L)
println("World!")
}
println("Hello,")
}
GlobalScope.launch{} 会创建一个顶级协程 尽管和轻量 但是还会消耗一些内存资源 如果开发者忘记保留对该协程的引用 这样的话该协程就会一直运行 直到整个应用程序停止才结束
使用场景中经常遇到的场景:
- 协程中代码被挂起(延迟太久或者启动的协程太多)导致内存不足,此时我们就需要手动保留所有已启动协程的引用,以便于在需要的时候停止协程,但是这很容易就出错
上述实例,我们通过runBlocking将main函数转换成主协程 这样的话 每个 launch job协程都运行在主协程作用域中 在runBlocking作用域中的所有协程都会主动启动 无需join() 外部协程在runBlocking作用域中所有启动的协程为执行完之前不会结束 - launch函数是CoroutineScope的扩展函数 而runBlocking函数体中的参数也被声明为CoroutineScope的扩展函数,所以launch就隐式持有了和runBlocking相同的协程作用域,即使delay()再久也会被执行
作用域构造器
- coroutineScope 用于创建一个协程作用域 作用域中所有启动的协程都执行完毕才会结束
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
}
runBlocking{} 和 coroutineScope{}看起来很像,都是等待其作用域中所有启动的协程sou执行完毕后才会结束
两者的主要区别 runBlocking是阻塞当前线程的,而coroutineScope只是挂起并释放底层线程以供其它协程使用
所以runBlocking只是普通函数 coroutineScope是挂起函数
提取函数并重构
抽取launch代码块中的操作为一个独立的函数 需要将其声明为挂起函数 挂起函数可以向常规函数一样在协程中使用,其额外的特性:可以依次使用其它挂起函数(join() delay()等)来使协程挂起
fun main() = runBlocking {
launch { doWorld() }
println("Hello,")
}
// this is your first suspending function
suspend fun doWorld() {
delay(1000L)
println("World!")
}
协程轻量级
- 主要体现在创建速度 创建相同数量的线程和协程 会发现线程的创建会很慢 而协程的创建是相同数量线程创建的10几倍
fun main() = runBlocking {
repeat(100...000) { // launch a lot of coroutines
launch {
// delay(100L)
print(".")
}
}
}
全局协程类似于守护线程
suspend fun main() {
// 相当于守护线程 主线程结束 守护线程也会随之消亡
GlobalScope.launch {
repeat(1000) {
println(" 打印低 $it 次 ")
delay(500L)
}
}
delay(1200L)
}
fun main() = runBlocking {
launch {
repeat(1000) { i ->
println(" 打印低 $it 次 ")
delay(100L)
}
}
// 协程执行完毕 才会继续执行下面的函数
delay(1300L) // just quit after delay
}
GlobalScope作用域中的launch无法保持活动状态 当主线程结束时 守护线程launch也会随之消亡
runBlocking 作用域中的launch 会一直执行 等待所有的协程都执行完毕 才会往下继续执行
转载请标明出处 感觉有用,请点点赞,谢谢!