下面的函数运行正常,但我不明白为什么我的 Sonar 插件会对此行发出警告CoroutineScope(MDCContext()).launch { startPerformanceTest(request) }
。这是误报吗?我该如何消除它?
override suspend fun startPerformance(request: PerformanceStartRequest) {
if (!PerformanceTestLock.tryAcquireLock(request.taskId)) {
throw BizException.of(RunnerException.SCRIPT_EXCLUSIVE)
}
CoroutineScope(MDCContext()).launch {
// Remove this dispatcher. It is pointless when used with only suspending functions.
// kotlin:S6311
startPerformanceTest(request)
}
}
private suspend fun startPerformanceTest(request: PerformanceStartRequest) = with(request) {
try {
val result = performanceDomainService.execute(taskId, script, runners)
.apply {
instanceMetrics = metricDomainService.queryInstanceMetric(services, startTime, endTime)
}
messageDomainService.sendSuccessMessage(taskId, Phase.PERFORMANCE_TEST, result)
} catch (e: Exception) {
logger.error(e) { "Performance test failed!" }
messageDomainService.sendFailureMessage(taskId, Phase.PERFORMANCE_TEST, e.message)
} finally {
PerformanceTestLock.releaseLock(taskId)
}
}
如果你点击该问题,你就会得到为什么这是有问题的完整解释。
总结一下:按照惯例,每个挂起函数都负责使用适当的上下文/调度程序。因此,将挂起函数的调用移到另一个调度程序是没有意义的。这应该在函数内部完成,在实际需要的地方完成。
但是您的代码中存在更紧迫的问题。暂停函数绝不应该突然启动新的协程。这违反了结构化并发的原则。如果将函数运行的当前协程移动到另一个线程(只需使用)是不够的
withContext()
,您需要显式传递一个,CoroutineScope
然后可以使用它来代替您的临时范围CoroutineScope(MDCContext())
来启动新的协程。也许像这样,作为扩展函数:这样,调用者就始终能够通过取消该作用域来取消所有协程。使用 创建的作用域则无法实现这一点。如果调用者需要为此
CoroutineScope
创建新的coroutineScope
作用域,请使用(从较低的 开始c
):请注意,
coroutineScope
会阻止进一步执行,直到在其中启动的所有协程都完成为止。这样,的调用者caller
就不需要引用此范围,它只需取消当前协程,因为caller
仍在那里运行。这也允许自动取消新创建的范围。奇怪的是,在我当前的设置中,这仍然会引发 Sonar 问题
kotlin:S6311
。情况不应该如此,因为该位置不再涉及调度员。这似乎是一个错误,因为即使上面链接中显示的兼容解决方案仍然会产生相同的问题。