如何在不中断 Kotlin 协程计算的情况下实现超时?
How to implement timeout without interrupting a calculation in Kotlin coroutine?
假设一个请求开始了长时间的计算,但等待结果的时间不会超过 X 秒。但是我不想中断计算,而是希望它并行继续直到完成。
withTimeoutOrNull
函数满足第一个条件(“等待时间不超过”)。
当结果准备好时,继续计算并执行某些最终操作的标准(惯用的 Kotlin)方法是什么?
示例:
fun longCalculation(key: Int): String { /* 2..10 seconds */}
// ----------------------------
cache = Cache<Int, String>()
suspend fun getValue(key: Int) = coroutineScope {
val value: String? = softTimeoutOrNull(
timeout = 5.seconds(),
calculation = { longCalculation() },
finalAction = { v -> cache.put(key, v) }
)
// return calculated value or null
}
这是一个足够小众的案例,我认为没有就惯用的方法达成共识。
由于您希望即使当前协程正在恢复也能在后台继续工作,因此您需要一个单独的 CoroutineScope 来启动该后台工作,而不是使用 coroutineScope
构建器来启动协程作为当前协程的子协程。该外部范围将决定它启动的协程的生命周期(如果被取消,则取消它们)。通常,如果您使用的是协程,那么您手头已经有了一个与当前 class.
的生命周期关联的范围
我认为这可以满足您的描述。当前协程可以在 withTimeoutOrNull
中包装一个 Deferred.await()
调用,看看它是否可以等待另一个协程(这不是子协程,因为它是直接从外部 CoroutineScope 启动的)而不干扰它。
suspend fun getValue(key: Int): String? {
val deferred = someScope.async {
longCalculation(key)
.also { cache.put(key, it) }
}
return withTimeoutOrNull(5000) { deferred.await() }
}
这是一个通用版本:
/**
* Launches a coroutine in the specified [scope] to perform the given [calculation]
* and [finalAction] with that calculation's result. Returns the result of the
* calculation if it is available within [timeout].
*/
suspend fun <T> softTimeoutOrNull(
scope: CoroutineScope,
timeout: Duration,
calculation: suspend () -> T,
finalAction: suspend (T) -> Unit = { }
): T? {
val deferred = scope.async {
calculation().also { finalAction(it) }
}
return withTimeoutOrNull(timeout) { deferred.await() }
}
假设一个请求开始了长时间的计算,但等待结果的时间不会超过 X 秒。但是我不想中断计算,而是希望它并行继续直到完成。
withTimeoutOrNull
函数满足第一个条件(“等待时间不超过”)。
当结果准备好时,继续计算并执行某些最终操作的标准(惯用的 Kotlin)方法是什么?
示例:
fun longCalculation(key: Int): String { /* 2..10 seconds */}
// ----------------------------
cache = Cache<Int, String>()
suspend fun getValue(key: Int) = coroutineScope {
val value: String? = softTimeoutOrNull(
timeout = 5.seconds(),
calculation = { longCalculation() },
finalAction = { v -> cache.put(key, v) }
)
// return calculated value or null
}
这是一个足够小众的案例,我认为没有就惯用的方法达成共识。
由于您希望即使当前协程正在恢复也能在后台继续工作,因此您需要一个单独的 CoroutineScope 来启动该后台工作,而不是使用 coroutineScope
构建器来启动协程作为当前协程的子协程。该外部范围将决定它启动的协程的生命周期(如果被取消,则取消它们)。通常,如果您使用的是协程,那么您手头已经有了一个与当前 class.
我认为这可以满足您的描述。当前协程可以在 withTimeoutOrNull
中包装一个 Deferred.await()
调用,看看它是否可以等待另一个协程(这不是子协程,因为它是直接从外部 CoroutineScope 启动的)而不干扰它。
suspend fun getValue(key: Int): String? {
val deferred = someScope.async {
longCalculation(key)
.also { cache.put(key, it) }
}
return withTimeoutOrNull(5000) { deferred.await() }
}
这是一个通用版本:
/**
* Launches a coroutine in the specified [scope] to perform the given [calculation]
* and [finalAction] with that calculation's result. Returns the result of the
* calculation if it is available within [timeout].
*/
suspend fun <T> softTimeoutOrNull(
scope: CoroutineScope,
timeout: Duration,
calculation: suspend () -> T,
finalAction: suspend (T) -> Unit = { }
): T? {
val deferred = scope.async {
calculation().also { finalAction(it) }
}
return withTimeoutOrNull(timeout) { deferred.await() }
}