android - 如何取消协程中的阻塞代码

标签 android asynchronous kotlin kotlin-coroutines

我有以下代码结构:

 @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/

相关文章:

android - 在 android 小部件中显示 fragment

android - 使用 Fuel 和 Result 将字节数组转换为 Json

android - onItemClick listView 关闭一个,最后单击的项目导致崩溃?

Java 9 HttpClient : Correctly handle async response

android - 如何更改 Xamarin for android 上的条目?

javascript - 如何解析$q.all?

android - 如何让 Android 服务每隔几秒广播一次 Intent ?

android - 为 kotlin 扩展函数的基础对象附加上下文

javascript - 如何下载图像并将其显示在 NativeScript 应用程序中?

android - 如何制作像 Chrome 中那样的溢出菜单?