协程学习(七)协程简单的使用之父子协程的层级关系

在前几篇文章都是在介绍协程的作用域,也经常提及协程的父子关系,那么什么样的协程关系才是父子协程关系呢

先看如下代码

kotlin 复制代码
@JvmStatic
fun main(array: Array<String>){
    //顶级父协程
    runBlocking {
        //子协程1
        launch {
            delay(1000)
            println("launch 1 子协程执行完毕")
        }

        //子协程2
        launch {
            delay(2000)
            println("launch 2 子协程执行完毕")
        }
    }
}
结果:
launch 1 子协程执行完毕
launch 2 子协程执行完毕

Process finished with exit code 0

可以发现,在 runBlocking 协程作用域内,我们创建了2个新的协程,这两个协程都进行了 delay,虽然 runBlocking 没有等待 launch 1 与 launch 2 的执行完毕,但是结果都被打印出来了,这就证明 launch 1 与 launch 2 协程被 顶级父协程所管理,这种现象就是 父子协程关系,那么如何建立父子协程关系呢,

想要创建父子协程关系,子协程必须创建在父协程的作用域内,并且创建时不能重新指定 CoroutineContext ,否则 新创建的协程将构建自己的协程作用域,从而升级与外部协程并立成兄弟协程

为了验证上面这段话,现在将例子重新修改一下,为了让大家看得更直观,例子里面包含了一个完整版本,一个缩写版本

scss 复制代码
@JvmStatic
fun main(array: Array<String>){
    //顶级父协程
    runBlocking {
        //缩写版(由于第一个参数就是 context ,我们可以省略 context = )-->由于重新指定了 CoroutineContext ,升级为 runBlocking 的兄弟协程
        launch(Job()) {
            delay(1000)
            println("launch 1 协程执行完毕")
        }

        //由于重新指定了 CoroutineContext ,升级为 runBlocking 的兄弟协程
        launch(context = Job()) {
            delay(2000)
            println("launch 2 协程执行完毕")
        }
    }
}

结果:

Process finished with exit code 0

发现在重新指定了context 是Job()的情况下, launch 1 与 launch 2 都没有重新打印,那么是不是只要替换了 CoroutineContext 就肯定会出现何种情况呢

看看下面这种情况

scss 复制代码
@JvmStatic
fun main(array: Array<String>){
    //顶级父协程
    runBlocking(Dispatchers.Default){
        //子协程1
        launch(Dispatchers.IO) {
            delay(1000)
            println("launch 1 子协程执行完毕")
        }

        //子协程2
        launch(context = Dispatchers.IO) {
            delay(2000)
            println("launch 2 子协程执行完毕")
        }
    }
}
结果:
launch 1 子协程执行完毕
launch 2 子协程执行完毕

Process finished with exit code 0

指定 runBlocking 的context 是 Dispatchers.Default,重新指定子协程的context 是 Dispatchers.IO,所有协程的结果都能打印出来,这里还不能确定,我们现在让 runBlocking 在0.5s后取消,看看 launch 1 与 launch 2 的表现

scss 复制代码
@JvmStatic
fun main(array: Array<String>){
    //顶级父协程
    runBlocking(Dispatchers.Default){
        //子协程1
        launch(Dispatchers.IO) {
            delay(1000)
            println("launch 1 子协程执行完毕")
        }

        //子协程2
        launch(context = Dispatchers.IO) {
            delay(2000)
            println("launch 2 子协程执行完毕")
        }
        delay(500)
        cancel()
    }
}
结果:
Exception in thread "main" kotlinx.coroutines.JobCancellationException: BlockingCoroutine was cancelled; job="coroutine#1":BlockingCoroutine{Cancelled}@e2144e4
	at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:1555)
	at kotlinx.coroutines.CoroutineScopeKt.cancel(CoroutineScope.kt:287)
	at kotlinx.coroutines.CoroutineScopeKt.cancel$default(CoroutineScope.kt:285)
	at com.tsm.opencv.Tsm$main$1.invokeSuspend(Tsm.kt:43)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)

Process finished with exit code 1

可以看到父协程被取消 launch 1 与 launch 2 同时也被取消了,即可证明他们现在也是父子协程关系

再来看下面的例子

scss 复制代码
@JvmStatic
fun main(array: Array<String>){
    //顶级父协程
    runBlocking{
        println(Thread.currentThread().name)

        //子协程1
        launch {
            withContext(Dispatchers.IO){
                delay(1000)
                println(Thread.currentThread().name)
            }
            println("launch 1 子协程执行完毕")
        }

        //子协程2
        launch {
            withContext(Dispatchers.IO){
                delay(1000)
                println(Thread.currentThread().name)
            }
            println("launch 2 子协程执行完毕")
        }
    }
}
结果:
main @coroutine#1
DefaultDispatcher-worker-1 @coroutine#3
DefaultDispatcher-worker-3 @coroutine#2
launch 2 子协程执行完毕
launch 1 子协程执行完毕

Process finished with exit code 0

为什么已经在子协程中使用了 Dispatchers.IO ,但是 runBlocking 还是会等待 launch 1 与 launch 2 这两个协程呢, 我们先来一步一步分析 1: runBlocking 是在主线程中创建的,所以我们第一行打印的线程名称是 main

2: 我们使用 runBlocking 的作用域 CoroutineScope 创建了子协程 launch 1 与 launch 2

3:我们调用 withContext 方法,并指定了 Dispatchers.IO ,注意 withContext 是一个方法,并不是重新创建一个协程,

所以建立父子协程关系的条件就是 子协程使用外部协程的作用域 CoroutineScope ,并且不重写 CoroutineContext , 但是从上面的例子中发现有一个特殊的,那就是 父协程 CoroutineContext 只是 Dispatchers.Default ,而子协程只使用 Dispatchers.IO 后,他们仍然是父子协程关系

为了证明如果父协程中除了指定 Dispatchers.Default外,增加其他参数条件就不成立,我又修改了一下这个案例

kotlin 复制代码
 @JvmStatic
fun main(array: Array<String>){
         runBlocking {
             var error=CoroutineExceptionHandler { _, e ->
                 println("e:$e")
             }
             var name=CoroutineName("tsm")
             var context= name + Dispatchers.IO + error
             //由于使用了新的 CoroutineContext 升级成为runBlocking的兄弟协程,  内部launch父协程
             CoroutineScope(context).launch {
                 launch(name+Dispatchers.Default+error) {
                     //子协程
                     delay(3000)
                 }
             }
             delay(2000)
         }
 }
 结果:
 Process finished with exit code 0

了解了协程作用域,协程父子关系,协程CoroutineContext的传播机制,我们就可以去了解协程的异常了,由于协程异常的特殊性,所有需要非常多的铺垫

相关推荐
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第8课 资源管理与主题
android·windows·学习·ios·kotlin·web·composer
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第9课 测试与调试
android·学习·macos·ios·kotlin·web·composer
传奇开心果编程3 小时前
【Compose Multiplatform 跨端开发学与练】第4课 导航与路由
android·windows·学习·ui·ios·kotlin·composer
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第6课 状态管理与架构
android·学习·ui·ios·架构·kotlin·composer
事圆则缓4 小时前
Android AOSP 定制常见概念:源码目录、系统镜像与刷机流程
android
传奇开心果编程4 小时前
【Compose Multiplatform 跨端开发学与练】第2课 Compose 基础语法
android·windows·学习·ui·ios·kotlin·composer
supabc1234 小时前
Celium:连接 Windows、Mac、Linux 与 Android,让远程访问和设备管理更简单
android·linux·windows·macos·远程访问·网络管理·celium
Dovis(誓平步青云)4 小时前
导览音频切换太快,旧讲解不能覆盖新展品
android·前端·javascript·ecmascript·音视频·宠物
五彩小白21 小时前
自回归和上下文
android
事圆则缓1 天前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android