MST

星途 面试题库

面试题:Kotlin协程异常处理之高级难度

假设在一个复杂的Kotlin协程任务链中,存在多个子协程,其中一个子协程抛出异常,如何确保异常能够被正确捕获并处理,同时避免其他子协程无意义的继续执行,并且保证资源的正确释放?请给出详细的代码示例及说明。
38.6万 热度难度
编程语言Kotlin

知识考点

AI 面试

面试题答案

一键面试
  1. 使用try - catch块捕获异常: 在Kotlin协程中,可以在try - catch块内启动协程,这样当协程抛出异常时,catch块可以捕获到异常。 同时,为了避免其他子协程无意义的继续执行,可以使用Job来管理协程的生命周期,当一个子协程出错时,取消其他相关的子协程。 对于资源释放,可以使用finally块来确保资源无论是否发生异常都能正确释放。

  2. 代码示例

import kotlinx.coroutines.*

// 模拟需要释放的资源
class Resource : AutoCloseable {
    init {
        println("Resource created")
    }

    override fun close() {
        println("Resource closed")
    }
}

fun main() = runBlocking {
    val job = Job()
    val scope = CoroutineScope(Dispatchers.Default + job)

    val resource = Resource()
    try {
        val deferred1 = scope.async {
            // 模拟第一个子协程任务
            delay(1000)
            println("Sub - coroutine 1 is running")
            throw RuntimeException("Sub - coroutine 1 error")
        }

        val deferred2 = scope.async {
            // 模拟第二个子协程任务
            delay(1500)
            println("Sub - coroutine 2 is running")
        }

        deferred1.await()
        deferred2.await()
    } catch (e: Exception) {
        println("Caught exception: $e")
        job.cancel() // 取消其他子协程
    } finally {
        resource.close() // 释放资源
    }
}
  1. 代码说明
    • 首先定义了一个Resource类,实现了AutoCloseable接口,用于模拟需要释放的资源。
    • main函数中,使用runBlocking创建了一个顶层协程。
    • 创建了一个Job对象job,并基于它创建了一个CoroutineScope对象scope,这个scope的生命周期与job相关联。
    • try块中启动了两个子协程deferred1deferred2deferred1故意抛出一个异常来模拟错误情况。
    • deferred1.await()deferred2.await()用于等待子协程执行完成。如果deferred1抛出异常,await()会将异常传递出来。
    • catch块中,捕获到异常后打印异常信息,并调用job.cancel()取消其他子协程(这里就是deferred2),避免其无意义的继续执行。
    • finally块中,调用resource.close()释放资源,确保无论是否发生异常,资源都能被正确释放。