此代码:
fun main() {
runBlocking {
try {
val deferred = async { throw Exception() }
deferred.await()
} catch (e: Exception) {
println("Caught $e")
}
}
println("Completed")
}
结果如下:
Caught java.lang.Exception
Exception in thread "main" java.lang.Exception
at org.mtopol.TestKt$main$1$deferred$1.invokeSuspend(test.kt:11)
...
这种行为对我来说没有意义。异常已被捕获并处理,但它仍作为未处理的异常逃逸到顶层。
这种行为是否有文件记录和预期?这违背了我对异常处理应该如何工作的所有直觉。
这个问题我改编自静态编程语言论坛上的一个帖子。
Kotlin文档建议,如果我们不想在一个协同程序失败时取消所有协同程序,就使用supervisorScope
。这样我就可以写作了
fun main() {
runBlocking {
supervisorScope {
try {
launch {
delay(1000)
println("Done after delay")
}
val job = launch {
throw Exception()
}
job.join()
} catch (e: Exception) {
println("Caught $e")
}
}
}
println("Completed")
}
输出现在是
Exception in thread "main" java.lang.Exception
at org.mtopol.TestKt$main$2$1$job$1.invokeSuspend(test.kt:16)
...
at org.mtopol.TestKt.main(test.kt:8)
...
Done after delay
Completed
这又不是我想要的行为。这里,一个启动
ed协程失败,出现了一个未处理的异常,使其他协程的工作无效,但它们不间断地进行。
我认为合理的行为是当协程以不可预见的(即未处理的)方式失败时分散取消。从wait
捕获异常意味着没有任何全局错误,只是作为业务逻辑的一部分处理的本地化异常。
这可以通过稍微改变代码来解决,使延迟
值使用与运行阻塞
作用域相同的CoroutineContext
显式执行。
runBlocking {
try {
val deferred = withContext(this.coroutineContext) {
async {
throw Exception()
}
}
deferred.await()
} catch (e: Exception) {
println("Caught $e")
}
}
println("Completed")
更新原始问题后更新
这是否提供您想要的:
runBlocking {
supervisorScope {
try {
val a = async {
delay(1000)
println("Done after delay")
}
val b = async { throw Exception() }
awaitAll(a, b)
} catch (e: Exception) {
println("Caught $e")
// Optional next line, depending on whether you want the async with the delay in it to be cancelled.
coroutineContext.cancelChildren()
}
}
}
这是从这篇讨论并行分解的评论中摘取的。
虽然所有的答案都是正确的,但让我来解释一下,这可能会帮助其他用户。此处(官方文件)记录如下:-
如果协同程序遇到除CancellationException
之外的异常,它会用该异常取消其父进程。此行为不能被重写,并用于为结构化并发提供稳定的协同路由层次结构,该层次结构不依赖于协同路由ExceptionHandler实现。当父级(在GlobalScope中)的所有子级终止时,原始异常由父级(在GlobalScope中)处理。
将异常处理程序安装到在主runBlocking范围内启动的协程中是没有意义的,因为尽管安装了异常处理程序,但当其子进程异常完成时,主协程总是会被取消。
希望这能有所帮助。
在研究了Kotlin引入这种行为的原因后,我发现,如果异常不是以这种方式传播的,那么编写及时取消的行为良好的代码会很复杂。例如:
runBlocking {
val deferredA = async {
Thread.sleep(10_000)
println("Done after delay")
1
}
val deferredB = async<Int> { throw Exception() }
println(deferredA.await() + deferredB.await())
}
因为a
是我们碰巧等待的第一个结果,所以此代码将持续运行10秒,然后导致错误,并且没有完成任何有用的工作。在大多数情况下,我们希望在一个组件出现故障时立即取消所有操作。我们可以这样做:
val (a, b) = awaitAll(deferredA, deferredB)
println(a + b)
这段代码不那么优雅:我们被迫在同一个位置等待所有结果,我们失去了类型安全性,因为awaitAll
返回所有参数的公共超类型列表。如果我们有
suspend fun suspendFun(): Int {
delay(10_000)
return 2
}
我们想要写作
val c = suspendFun()
val (a, b) = awaitAll(deferredA, deferredB)
println(a + b + c)
在suspendFun
完成之前,我们被剥夺了摆脱困境的机会。我们可以这样做:
val deferredC = async { suspendFun() }
val (a, b, c) = awaitAll(deferredA, deferredB, deferredC)
println(a + b + c)
但这是脆弱的,因为你必须小心,以确保你这样做的每一个暂停调用。这也违背了静态编程语言“默认顺序”的原则
总而言之:目前的设计虽然一开始有悖常理,但作为一个实用的解决方案,它确实有意义。它还加强了不使用async await
的规则,除非您正在对任务进行并行分解。
我的RMI服务器接口声明了一个方法foo(),该方法被声明为引发RemoteException和Exception,如下所示: 服务器实现为: 我的客户端在服务器上调用foo: 现在,当我运行客户端时,我得到: 从java类型的foo()中获取异常。rmi。异常异常:未声明的检查异常;嵌套的例外是:java。伊奥。InterruptedIOException:操作超时 Java文档是这样说的。rm
下面是我在Spring boot中的ExceptionHandler类
我正在尝试创建一个过滤器来处理异常(请参见在JSF中处理未捕获的异常) 我在日志中看到错误: 我做错了什么?
我正在实现自定义'AuthenticationProvider'。如果没有经过身份验证,我将在'authenticate'函数中抛出异常,如下所示。 我有全局异常处理程序,如下所示。 当在'authenticate'函数内部引发异常时,不会调用全局异常处理程序。对于所有其他例外情况,它正在被调用。我想在全局异常处理程序中捕获异常并返回自定义错误消息。我怎么能那样做?感谢任何帮助。提前道谢。
下面是我的代码片段: 现在java用'unhandled exception type SQLException)标记指示语句(以及后面的所有语句)。gfsql.dosql抛出此异常并定义为: public ResultSet doSQL(String sqlCommand)抛出SQLException{ 有趣的是--如果我像这样重复“catch”块: 未标记“未处理”错误。(但是,重复的catc
问题内容: 关于程序流,这一直困扰着我一段时间。 我想知道是否有可能从Method中捕获错误,以阻止它执行通常无法跟着它执行的Method,就像下面我无法工作的示例所示。 我通常会有一个静态int变量,该变量会在程序运行时初始化为0,然后,如果某个方法捕获到异常,它将使int递增,并且每个方法仅在int为0时才运行。 这行得通,但我只是想知道是否可以用异常处理替换int shindig。 问题答案