美文网首页Kotlin
Kotlin-协程基础

Kotlin-协程基础

作者: 码农修行之路 | 来源:发表于2020-11-13 19:38 被阅读0次
    协程基础.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-协程基础

        本文链接:https://www.haomeiwen.com/subject/rrsbbktx.html