我有以下代码结构:
@Throws(InterruptedException::class)
fun method() {
// do some blocking operations like Thread.sleep(...)
}
var job = launch {
method()
}
job.cancelAndJoin()
方法
由外部库提供,我无法控制它的行为。它可能会花费很多时间来执行,因此在某些情况下应该通过超时取消。
我可以使用 kotlin coroutines 库提供的 withTimeout
函数,但由于协程设计,它无法取消带有阻塞的代码。有一些解决方法吗?
最佳答案
主要思想是使用协程外上下文线程池,JVM 线程可以在旧样式中中断,并从协程执行中订阅取消事件。当事件被 invokeOnCancellation
捕获时,我们可以中断当前线程。
实现:
val externalThreadPool = Executors.newCachedThreadPool()
suspend fun <T> withTimeoutOrInterrupt(timeMillis: Long, block: () -> T) {
withTimeout(timeMillis) {
suspendCancellableCoroutine<Unit> { cont ->
val future = externalThreadPool.submit {
try {
block()
cont.resumeWith(Result.success(Unit))
} catch (e: InterruptedException) {
cont.resumeWithException(CancellationException())
} catch (e: Throwable) {
cont.resumeWithException(e);
}
}
cont.invokeOnCancellation {
future.cancel(true)
}
}
}
}
它提供与通常的 withTimeout
类似的行为,但它还支持运行带有阻塞的代码。
注意:只有当您知道内部代码使用阻塞并且可以正确处理抛出的 InterruptedException
时才应调用它。在大多数情况下,首选 withTimeout
函数。
更新:自协程版本 1.3.7 以来,出现了一个新函数 runInterruptible
,它提供了相同的行为。所以这段代码可以简化:
suspend fun <T> withTimeoutOrInterrupt(timeMillis: Long, block: () -> T) {
withTimeout(timeMillis) {
runInterruptible(Dispatchers.IO) {
block()
}
}
}
关于android - 如何取消协程中的阻塞代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58402107/