首页 > 解决方案 > 为什么 CoroutineExceptionHandler 没有捕获/处理我的异常?

问题描述

在这段代码中,为什么handler只打印堆栈跟踪JobCancellationException而不是SocketException?里面的foo函数launch肯定是 throws SocketException,那么它会发生什么?

suspend fun foo() {
  val job = coroutineContext[Job]!!
  val socket = Socket()

  job.invokeOnCompletion(onCancelling = true) {
    if (!socket.isClosed) {
      socket.close()
    }
  }

  // non-routable address -> timeout
  // will throw SocketException after socket.close() is called above
  socket.connect(InetSocketAddress("10.0.0.0", 1234), 2000)
}

fun test() = runBlocking {
  val handler = CoroutineExceptionHandler { _, throwable ->
    throwable.printStackTrace()
  }

  val job = launch(DefaultDispatcher + handler) {
    foo()
  }

  delay(100)
  job.cancelAndJoin()
  delay(100)
}

标签: kotlinkotlinx.coroutines

解决方案


我不能告诉你为什么CoroutineExceptionHandler没有捕捉到抛出的异常launch。但我可以告诉你两件事——

  1. 我验证了您发现的行为 - 您是正确的,没有捕获到异常。
  2. 通过实验,我学会了如何在CoroutineExceptionHandler.

这是显示如何捕获它的代码:

fun f() = runBlocking {
    val eh = CoroutineExceptionHandler { _, e -> trace("exception handler: $e") }
    val cs1 = CoroutineScope(Dispatchers.Default)
    val j1 = cs1.launch(eh + CoroutineName("first"))  {
        trace("launched")
        delay(1000)
        throw RuntimeException("error!")
    }
    trace("joining j1")
    j1.join()
    val cs2 = CoroutineScope(Dispatchers.Default + eh)
    val j2 = cs2.launch(CoroutineName("second"))  {
        trace("launched")
        delay(1000)
        throw RuntimeException("error!")
    }
    trace("joining j2")
    j2.join()
    trace("after join")
}
f()

控制台输出:

[main @coroutine#1]: joining j1
[DefaultDispatcher-worker-1 @first#2]: launched
[DefaultDispatcher-worker-1 @first#2]: exception handler: java.lang.RuntimeException: error!
[main @coroutine#1]: joining j2
[DefaultDispatcher-worker-1 @second#3]: launched
[DefaultDispatcher-worker-3 @second#3]: exception handler: java.lang.RuntimeException: error!
[main @coroutine#1]: after join

关键要点是,如果您调用launchcustom CoroutineScope,任何CoroutineExceptionHandler直接提供给CoroutineScope构造函数或 to 的launch内容都会在launched 协程中引发异常时执行。

希望有帮助!!

更新

我发现了为什么没有捕获异常。在这里查看我的答案。


推荐阅读