Kotlin-协程基础

协程基础.jpg

第一个协程

根据官方文档 可以了解到 以下例子:

fun main() {
    GlobleScope.launch {// 启动一个协程 并继续执行后续代码,相当于守护线程
        println("第一个协程!")
    }
    println("主线程执行结束!")
}

上面的例子 运行后 :

  1. 为何直接打印“主线程执行结束”而没有执行协程里面的打印呢?
    协程的执行并不影响其它后续代码的执行,也就是说后面代码执行完,主线程就结束了,因此launch也随之消亡
  2. 怎样让协程先执行完,再执行后面的代码呢?
    可以在最后面添加非阻塞或者阻塞延迟 以此来让协程在主线程结束之前有足够的执行时间,代码如下:
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{} 会创建一个顶级协程 尽管和轻量 但是还会消耗一些内存资源 如果开发者忘记保留对该协程的引用 这样的话该协程就会一直运行 直到整个应用程序停止才结束
使用场景中经常遇到的场景:

  1. 协程中代码被挂起(延迟太久或者启动的协程太多)导致内存不足,此时我们就需要手动保留所有已启动协程的引用,以便于在需要的时候停止协程,但是这很容易就出错
    上述实例,我们通过runBlocking将main函数转换成主协程 这样的话 每个 launch job协程都运行在主协程作用域中 在runBlocking作用域中的所有协程都会主动启动 无需join() 外部协程在runBlocking作用域中所有启动的协程为执行完之前不会结束
  2. 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 会一直执行 等待所有的协程都执行完毕 才会往下继续执行

转载请标明出处 感觉有用,请点点赞,谢谢!

你可能感兴趣的:(Kotlin-协程基础)