kotlin 协程上下文异常处理详解

目录
  • 引言
  • 一、协程上下文
    • 1.CoroutineContext
    • 2.CorountineScope
    • 3.子协程继承父协程
  • 二、协程的异常传递
    • 1.协程的异常传播
    • 2.不同上下文(没有继承关系)之间协程异常会怎么样?
    • 3.向用户暴露异常
  • 三、协程的异常处理
    • 使用SupervisorJob
    • 异常捕获器CoroutineExceptionHandler
  • Android中全局异常的处理

引言

从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达到这种效果的原因就是协程上下文,上下文贯穿了协程的生命周期,这套思想和我们app的上下文很像

在开始真正了解协程上下文之前,我们先来看看下面的例子

下面的图代表了一个协程a的生命,就像一条从上至下的直线,它的生命只有100ms

当我们在a协程延迟函数100ms之前开启一个子协程b,b做了200ms的事情,如果不考虑调度消耗的时间,那么a协程的生命也会延长成200ms

代码验证下:

fun `test context life`() = runBlocking {
    //定义一个作用域
    val a = CoroutineScope(Dispatchers.Default)
    val startTime = System.currentTimeMillis()
    //协程a开启
    val jobA = a.launch {
        //子协程b开启
        val jobB = launch {
            delay(200)
        }
        delay(100)
    }
    //等待协程a结束
    jobA.join()
    val endTime = System.currentTimeMillis()
    println(endTime - startTime)
}
fun main() {
    `test context life`()
}

结果:237

如果我们把子协程b增加到delay 300ms,那么结果也会相应的变为:

323

通过上面的列子,来对协程上下文的有一个初步概念:可以说协程的生命周期,就是上下文的生命周期

协程拥有很多新的概念,很多人一开始接触就能难理解(包括我自己),这些概念都是在上下文的基础上引申而来的,所以我一再强调它的重要性,协程的上下文必须理解透,才能玩好协程,接下来我们来真正了解协程上下文

一、协程上下文

1.CoroutineContext

协程上下文有以下几项构成,它们都是实现了CoroutineContext.Element接口,有些是实现了AbstractCoroutineContextElement接口,而AbstractCoroutineContextElement继承CoroutineContext.Element接口

1.Job:控制协程的生命周期,也是我们能拿到操作协程任务的唯一对象

2.CoroutineDispatcher:就是之前介绍的调度器

3.CoroutineName:协程的名字,一般输出日志用的

4.CoroutineExceptionHandler:处理未捕获的异常

协程上下文实现了运算符重载,我们可以用+号来组合一个CoroutineContext的元素

2.CorountineScope

一般情况下,协程体内所有的子协程,都继承至根协程,协程的继承的关系不是我们所了解的类的继承关系,而是父协程和子协程的生命周期关系,还记得我们上面举得例子么,除非在协程体内自己手动创建协程作用域,即:创建一个全新的协程上下文,我们之前已经介绍过了:

CorountineScope:创建协程作用域,新起线程,观察源码,内部实际实例化的是ContextScope,ContextScope被internal修饰,内部使用,我们实例化不了

其他的实际上都是继承父协程上下文,或者内部实例化了ContextScope:

1.runBlocking:将主线程转变为协程,会阻塞主线程,实际上用的是一个EmptyCoroutineContext作为上下文,它是一个主线程的协程上下文,静态的全局变量,我们其实就可以理解成是主线程
2.GlobalScope:也是用的EmptyCoroutineContext
3.MainScope:使用ContextScope构造了新的上下文
4.coroutineScope:继承的父协程上下文,不能算是全新的协程
等等

3.子协程继承父协程

子协程继承父协程时,除了Job会自动创建新的实例外,其他3项的不手动指定的话,都会自动继承父协程的,Job对应的是协程任务,每次新的任务肯定都是新的Job对象

有了这些概念后,接下来通过代码,再熟悉巩固下

例子1:

fun `test context life1`() = runBlocking {
    //定义一个作用域
    val a = CoroutineScope(Dispatchers.Default)
    //协程a开启
    val jobA = a.launch {
        delay(100)
        println("jobA finished")
    }
    println("main finished")
}

结果:
main finished

由于a是一个根协程,全新的上下文,runBlocking 是主线程的协程上下文,所以当a开启任务时,不会阻塞主线程,当我们的进程都跑完了,jobA finished肯定不会打印了

例子2:

fun `test context life2`() = runBlocking {
    //定义一个作用域
    val a = CoroutineScope(Dispatchers.Default)
    //协程a开启
    val jobA = a.launch {
        delay(100)
        println("jobA finished")
    }
    jobA.join()
    println("main finished")
}

结果:
jobA finished
main finished

我们在主协程(主线程的协程)中,手动调用jobA的join方法,那么主线程就会阻塞,直到jobA执行完毕。这个和我们的多线程操作是一样的,主线程等待A线程执行完后再往后执行

例子3:

fun `test context life3`() = runBlocking {
    launch {
        delay(100)
        println("jobA finished")
    }
    println("main finished")
}

结果:
main finished
jobA finished

这回我们没有构建新的协程作用域,而是在根协程中直接使用子协程的方式,当然了,协程的上下文继承关系,使得我们的主协程等待子协程执行完毕后才结束生命

例子4:

fun `test context life4`() = runBlocking {
    launch(Dispatchers.IO + CoroutineName("jobA")) {
        delay(100)
        println("${coroutineContext[CoroutineName]}  finished")
    }
    println("main finished")
}

结果:
main finished
CoroutineName(jobA) finished

即使我们指定了子协程的调度器和协程名,也不会影响协程上下文继承关系,主协程还是会等待子协程执行完毕后才结束生命

如果你已经完全理解了,那么就可以知道以上例子使用async启动也是一样的效果

二、协程的异常传递

1.协程的异常传播

协程的异常传播也是遵循了协程上下文的机制,除了取消异常(CancellationException)外,当一个协程有了异常,如果没有主动捕获异常,那么异常会向上传播,直到根协程,子协程的异常都会导致根协程退出,自然其他子协程也会退出

例子1:

fun `test coroutineScope exception1`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = launch {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
    }
    delay(3000)
    println("finished")
}

结果:

job2 finished
Exception in thread "main" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception1$1$job2$1.invokeSuspend(exceptionTest.kt:46)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.ResumeModeKt.resumeMode(ResumeMode.kt:67)
    at kotlinx.coroutines.DispatchedKt.resume(Dispatched.kt:309)
    at kotlinx.coroutines.DispatchedKt.dispatch(Dispatched.kt:298)
    at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:250)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:260)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:332)
    at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.kt:298)
    at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)
    at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:80)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
    at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
    at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
    at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.test coroutineScope exception1(exceptionTest.kt:37)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.main(exceptionTest.kt:54)
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt.main(exceptionTest.kt)

Process finished with exit code 1

job2 1000ms后就发生了异常,导致job1和父协程都直接退出

2.不同上下文(没有继承关系)之间协程异常会怎么样?

例子1:

fun `test coroutineScope exception2`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).launch{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    delay(3000)
    println("finished")
}

结果:

job2 finished
Exception in thread "DefaultDispatcher-worker-2" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception1$1$job2$1.invokeSuspend(exceptionTest.kt:46)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
job finished
finished

Process finished with exit code 0

可以看出不同根协程的协程之间,异常并不会自动传递,我们的主线程上下文协程正常执行

再看例子2:

fun `test coroutineScope exception3`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    delay(3000)
    println("finished")
}

结果:
job2 finished
job finished
finished

和例子1的唯一区别是,使用了全新上下文的协程使用了async启动,哈哈,这就奇怪了,为什么会这样?

3.向用户暴露异常

还记得async启动的协程返回的是一个Deferred么,它可以使用await函数,来获取协程运行结果。那么试想一下,如果我就是想要一个协程执行完返回一个异常呢?

所以async中的异常会作为返回值,返回给调用await函数

fun `test coroutineScope exception4`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async{
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    job2.await()
    delay(3000)
    println("finished")
}

结果:

job2 finished
Exception in thread "main" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception4$1$job2$1.invokeSuspend(exceptionTest.kt:96)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)

Process finished with exit code 1

await的时候出现异常了,当然会导致协程退出,我们可以在await的时候捕获下这个异常,就不会影响主线程上下文的协程运行了

fun `test coroutineScope exception4`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = CoroutineScope(Dispatchers.IO).async {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
        println("new CoroutineScope finished")
    }
    try {
        job2.await()
    } catch (e: Exception) {
        e.printStackTrace()
    }
    delay(3000)
    println("finished")
}

结果:

job2 finished
java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception4$1$job2$1.invokeSuspend(exceptionTest.kt:96)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
job finished
finished

Process finished with exit code 0

值得注意的是,同一继承关系下的协程使用await并无法捕获异常,还是会遵循第一条,导致整个协程生命周期结束

fun `test coroutineScope exception5`() = runBlocking {
    val job2 = CoroutineScope(Dispatchers.IO).launch {
        val job1 = launch {
            delay(2000)
            println("job finished")
        }
        val job3 = async {
            delay(1000)
            println("job3 finished")
            throw IllegalArgumentException()
        }
        try {
            job3.await()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        delay(2000)
        println("job2 finished")
    }
    job2.join()
    println("finished")
}

结果:

job3 finished
java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception5$1$job2$1$job3$1.invokeSuspend(exceptionTest.kt:119)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test coroutineScope exception5$1$job2$1$job3$1.invokeSuspend(exceptionTest.kt:119)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

可以发现job3.await()的try catch并没有生效,所以向用户暴露异常只适用于不同上下文(没有继承关系)的协程

三、协程的异常处理

使用SupervisorJob

如果想要一个协程出现异常后,不影响其继承关系中的其他协程,可以使用SupervisorJob

fun `test SupervisorJob exception`() = runBlocking {
    val job1 = launch {
        delay(2000)
        println("job finished")
    }
    val job2 = async(SupervisorJob()) {
        delay(1000)
        println("job2 finished")
        throw IllegalArgumentException()
    }
    delay(3000)
    println("finished")
}

结果:
job2 finished
job finished
finished

可以看到,job2的异常并没有影响其他继承关系的协程的执行

SupervisorScope,这个我们前面已经用过了,就不重复介绍了

异常捕获器CoroutineExceptionHandler

协程上下文的4项之一,可以用CrashHandler理解,不过它并不能阻止协程的退出,只能够获取异常的信息

它使用有两个条件:

1.异常是自动抛出异常(launch)

2.实例化CoroutineScope的时候指定异常捕获器 或者 在一个根协程中

例子1:

fun `test SupervisorHandler exception1`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(handler)
    val job1 = scope.launch {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:
job2 finished
caught: java.lang.IllegalArgumentException
finished

job2抛出了异常,被捕获到了,但是scope的其他协程随之生命周期也都结束了

例子2:

fun `test SupervisorHandler exception2`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:
job2 finished
caught: java.lang.IllegalArgumentException
finished

和例子1相同,因为我们handler指定在了根协程

例子3:

fun `test SupervisorHandler exception3`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch {
        val job2 = launch(handler) {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        delay(2000)
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:

job2 finished
Exception in thread "DefaultDispatcher-worker-4" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test SupervisorHandler exception$1$job1$1$job2$1.invokeSuspend(exceptionTest.kt:161)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

handler不是在根协程中,不能捕获

如果一个子协程会抛出异常,那么对它进行等待时(join或await),包裹一层try catch 会出现意料之外的事

例子4:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
        delay(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:
job2 finished
job delay
caught: java.lang.IllegalArgumentException
finished

如果把scope根协程中的delay函数注释掉,会怎么样呢?

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
//        delay(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:
job2 finished
job delay
0
1
2
3
4
5
6
7
8
9
10
job finished
caught: java.lang.IllegalArgumentException

如果不包裹try catch 那么println("job delay")都不会执行

由例子4和例子5,我们可以推断,如果子协程有异常发生了,我们在等待时捕获异常后,根协程执行了挂起函数,那么它会直接中断,不执行挂起函数以下的代码,如果没有挂起函数,那么后面的代码还是会执行

为了加强验证这点,我们使用Thread.sleep(2000)替换delay函数测试下:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
//        val job3 = scope.launch {
//            println("job3 finished")
//        }
        println("job delay")
//        delay(2000)
        Thread.sleep(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果还是和例子5一样:
job2 finished
job delay
0
1
2
3
4
5
6
7
8
9
10
job finished
caught: java.lang.IllegalArgumentException
finished

Process finished with exit code 0

其实出现这个情况,和我们之前取消协程是一样的,出现异常后会开始取消协程,但是CPU密集型的代码还会执行,但是遇到挂起函数就会抛一个CancellationException,导致协程结束运行,如果我们在挂起函数加上try catch打印,那么我们就可以看到CancellationException了

例子6,把job3的注释放开:

fun `test SupervisorHandler exception4`() = runBlocking {
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("caught: $throwable")
    }
    val scope = CoroutineScope(Dispatchers.Default)
    val job1 = scope.launch(handler) {
        val job2 = launch {
            delay(1000)
            println("job2 finished")
            throw IllegalArgumentException()
        }
        try {
            job2.join()
        }catch (e:Exception){
        }
        val job3 = scope.launch {
            println("job3 finished")
        }
        println("job delay")
        delay(2000)
//        Thread.sleep(2000)
        for(i in 0..10){
            println(i)
        }
        println("job finished")
    }
    delay(4000)
    println("finished")
}

结果:

job2 finished
job delay
caught: java.lang.IllegalArgumentException
Exception in thread "DefaultDispatcher-worker-1" java.lang.IllegalArgumentException
    at com.aruba.mykotlinapplication.coroutine.ExceptionTestKt$test SupervisorHandler exception4$1$job1$1$job2$1.invokeSuspend(exceptionTest.kt:227)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:238)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
finished

Process finished with exit code 0

显然有异常没有被捕获,很明显这个异常是调用job3时输出的,由此又可以推断出,如果在等待任务结束时,任务出现异常并且手动捕获异常后,再启动子协程时,也会抛出异常,并且不可捕获
注意:新版本kotlin已修复这个bug,不会抛出异常了

Android中全局异常的处理

最后,感谢动脑学院Jason老师出的kotlin协程教程,得到了很多理解和启发

以上就是kotlin 协程上下文异常处理详解的详细内容,更多关于kotlin 协程上下文异常处理的资料请关注我们其它相关文章!

(0)

相关推荐

  • Kotlin协程flowOn与线程切换超详细示例介绍

    目录 示例代码 一.flowOn方法 1.ChannelFlowOperatorImpl类 二.collect方法 1.ChannelFlowOperator类的collect方法 2.ChannelFlow类的collect方法 3.flow方法中代码的执行 4.接收flow方法发出的值 三.flowOn方法与流的融合 四.总结 示例代码 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.flowOn(Dispatc

  • Kotlin协程launch原理详解

    目录 正文 launch使用 launch原理 CoroutineStart中找invoke方法 startCoroutineCancellable逻辑 小结 正文 launch我们经常用,今天来看看它是什么原理. 建议: 食用本篇文章之前记得先食用Kotlin协程之createCoroutine和startCoroutine launch使用 launch我们应该很熟悉了,随便举个例子: fun main() { val coroutineScope = CoroutineScope(Job(

  • Kotlin协程操作之创建启动挂起恢复详解

    目录 一.协程的创建 1.start方法 2.CoroutineStart类 3.startCoroutineCancellable方法 4.createCoroutineUnintercepted方法 5.createCoroutineFromSuspendFunction方法 二.协程的启动 1.ContinuationImpl类 2.resumeCancellableWith方法 3.BaseContinuationImpl类 4.invokeSuspend方法 三.协程的挂起与恢复 下面

  • Kotlin协程开发之Flow的融合与Channel容量及溢出策略介绍

    目录 一.协程间的通信 1.通道容量 2.溢出策略 二.FusibleFlow接口 三.ChannelFlow类 一.协程间的通信 当需要进行协程间的通信时,可以调用Channel方法,创建一个Channel接口指向的对象,通过调用该对象的send方法和receive方法实现消息的发送与接收.协程对Channel接口的实现,本质上与阻塞队列类似,这里不再赘述. 1.通道容量 事实上,send方法与receive方法并没有定义在Channel接口中,而是分别定义在SendChannel接口和Rec

  • Kotlin协程Dispatchers原理示例详解

    目录 前置知识 demo startCoroutineCancellable intercepted()函数 DefaultScheduler中找dispatch函数 Runnable传入 Worker线程执行逻辑 小结 前置知识 Kotlin协程不是什么空中阁楼,Kotlin源代码会被编译成class字节码文件,最终会运行到虚拟机中.所以从本质上讲,Kotlin和Java是类似的,都是可以编译产生class的语言,但最终还是会受到虚拟机的限制,它们的代码最终会在虚拟机上的某个线程上被执行. 之

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • Kotlin协程概念原理与使用万字梳理

    目录 一.协程概述 1.概念 2.特点 3.原理 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 三.协程使用 1.协程的启动 2.协程间通信 3.多路复用 4.序列生成器 5.协程异步流 6.全局上下文 一.协程概述 1.概念 协程是Coroutine的中文简称,co表示协同.协作,routine表示程序.协程可以理解为多个互相协作的程序.协程是轻量级的线程,它的轻量体现在启动和切换,协程的启动不需要申请额外的堆栈空间:协程的切换发生在

  • Kotlin协程的启动方式介绍

    目录 1.GlobalScope.launch 2.runBlocking 启动协程 3.async启动协程 启动协程的基本方式 1.GlobalScope.launch 代码示例: fun testGlobalScope() { GlobalScope.launch { println("Coroutinue started!") delay(1000L) println("Hello World!") } println("After launch!&

  • Kotlin协程启动createCoroutine及创建startCoroutine原理

    目录 createCoroutine 和 startCoroutine startCoroutine调用 createCoroutineUnintercepted intercepted resume 结语 createCoroutine 和 startCoroutine 协程到底是怎么创建和启动的?本篇文章带你揭晓. 在Continuation.kt文件中,有2个基础API,这里单独提出来说一下,方便后面我们理解launch. public fun <T> (suspend () ->

  • kotlin 协程上下文异常处理详解

    目录 引言 一.协程上下文 1.CoroutineContext 2.CorountineScope 3.子协程继承父协程 二.协程的异常传递 1.协程的异常传播 2.不同上下文(没有继承关系)之间协程异常会怎么样? 3.向用户暴露异常 三.协程的异常处理 使用SupervisorJob 异常捕获器CoroutineExceptionHandler Android中全局异常的处理 引言 从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达

  • Kotlin全局捕捉协程异常方法详解

    单个异常捕捉 val handler = CoroutineExceptionHandler { coroutineContext, throwable -> Log.d(TAG, "onCreate: handler${throwable}") } Log.d(TAG, "onCreate:1") findViewById<Button>(R.id.button).also { it.setOnClickListener { GlobalScop

  • Python中协程用法代码详解

    本文研究的主要是python中协程的相关问题,具体介绍如下. Num01–>协程的定义 协程,又称微线程,纤程.英文名Coroutine. 首先我们得知道协程是啥?协程其实可以认为是比线程更小的执行单元. 为啥说他是一个执行单元,因为他自带CPU上下文.这样只要在合适的时机, 我们可以把一个协程 切换到另一个协程. 只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的. Num02–>协程和线程的差异 那么这个过程看起来和线程差不多.其实不然, 线程切换从系统层面远不止保存和恢复 CP

  • python多任务之协程的使用详解

    1|0使用yield完成多任务 import time def test1(): while True: print("--1--") time.sleep(0.5) yield None def test2(): while True: print("--2--") time.sleep(0.5) yield None if __name__ == "__main__": t1 = test1() t2 = test2() while True

  • Kotlin协程上下文与上下文元素深入理解

    目录 一.EmptyCoroutineContext 二.CombinedContext 三.Key与Element 四.CoroutineContext 五.AbstractCoroutineContextKey与AbstractCoroutineContextElement 一.EmptyCoroutineContext EmptyCoroutineContext代表空上下文,由于自身为空,因此get方法的返回值是空的,fold方法直接返回传入的初始值,plus方法也是直接返回传入的cont

  • golang协程池设计详解

    Why Pool go自从出生就身带"高并发"的标签,其并发编程就是由groutine实现的,因其消耗资源低,性能高效,开发成本低的特性而被广泛应用到各种场景,例如服务端开发中使用的HTTP服务,在golang net/http包中,每一个被监听到的tcp链接都是由一个groutine去完成处理其上下文的,由此使得其拥有极其优秀的并发量吞吐量 for { // 监听tcp rw, e := l.Accept() if e != nil { ....... } tempDelay = 0

  • Python gevent协程切换实现详解

    一.背景 大家都知道gevent的机制是单线程+协程机制,当遇到可能会阻塞的操作时,就切换到可运行的协程中继续运行,以此来实现提交系统运行效率的目标,但是具体是怎么实现的呢?让我们直接从代码中看一下吧. 二.切换机制 让我们从socket的send.recv方法入手: def recv(self, *args): while 1: try: return self._sock.recv(*args) except error as ex: if ex.args[0] != EWOULDBLOCK

  • Kotlin协程的基础与使用示例详解

    目录 一.协程概述 1.概念 2.特点 3.原理 1)续体传递 2)状态机 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 1)协程状态的转换 2)状态标识的变化 三.协程使用 1.协程的启动 1)runBlocking方法 2)launch方法 3)async方法 4)suspend关键字 5)withContext方法 6)suspend方法 2.协程间通信 1)Channel 2)Channel的容量 3)produce方法与act

随机推荐