问题
I have the following code structure:
@Throws(InterruptedException::class)
fun method() {
// do some blocking operations like Thread.sleep(...)
}
var job = launch {
method()
}
job.cancelAndJoin()
The method
is provided by the external library and I can't control its behaviour. It can take a lot of time for execution, so in some cases it should be canceled by timeout.
I can use the withTimeout
function provided by the kotlin coroutines library, but it can't cancel a code with blockings due to the coroutines design. It there some workaround to do it?
回答1:
The main idea is to use the out of coroutines context thread pool with narutal threads that can be interrupted in the old style and subscribe on the cancellation event from the coroutines execution. When the event is catched by invokeOnCancellation
, we can interrupt the current thread.
The implementation:
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)
}
}
}
}
It provides the similar behaviour like usual withTimeout
, but it additionally supports running a code with blockings.
Note: It should be called only when you know, that the inner code use blockings and can correctly process a throwed InterruptedException
. In most cases the withTimeout
function is prefered.
来源:https://stackoverflow.com/questions/58402107/how-to-cancel-the-blocking-code-in-the-coroutines