使用withContext得到非阻塞代码时科特林协程withTimeout不会取消(kotlin c

2019-09-28 06:30发布

我使用withContext变换函数为不阻止调用线程暂停功能。 为此,我使用https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761作为参考。

现在我想调用该函数超时。 为此,我使用withTimeout调用的函数这样:

@Test
internal fun timeout() {
    runBlocking {
        logger.info("launching")
        try {
            withTimeout(1000) {
                execute()
            }
        } catch (e: TimeoutCancellationException) {
            logger.info("timed out", e)
        }
    }
}

private suspend fun execute() {
    withContext(Dispatchers.IO) {
        logger.info("sleeping")
        Thread.sleep(2000)
    }
}

所以,我会想到的是,在1000个米利斯异步启动协同程序被取消,TimeoutCancellationException被抛出。
但是,什么情况是,完整的2000米利斯传递并在完成协程时抛出异常:

14:46:29.231 [主@协程#1] INFO btccCoroutineControllerTest - 发射
14:46:29.250 [DefaultDispatcher工-1 @协程#1] INFO btccCoroutineControllerTest - 睡眠
14:46:31.261 [主@协程#1] INFO btccCoroutineControllerTest - 超时kotlinx.coroutines.TimeoutCancellationException:超时在kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:128)等待1000毫秒在kotlinx.coroutines.TimeoutCoroutine .RUN(Timeout.kt:94)在kotlinx.coroutines.EventLoopImplBase $ DelayedRunnableTask.run(EventLoop.kt:307)在kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.kt:116)在kotlinx.coroutines.DefaultExecutor.run( DefaultExecutor.kt:68)在java.lang.Thread.run(Thread.java:748)

我使用的是什么问题?

或许这是预期的行为? 在文档中的计数器也得到2,这意味着协程被取消之前1500个米利斯已经过去了: https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/cancellation-and-timeouts.md#timeout

Answer 1:

在取消重读的文件后,它似乎协同程序必须合作是撤销:

协同程序取消是合作。 一个协同程序代码必须合作,是撤销。

https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html#cancellation-is-cooperative

我还发现,通过设计线程没有得到打断:

取消协程并不会中断线程。 这是由设计,这样做是因为,不幸的是,许多Java库中不正确的中断线程操作。

https://discuss.kotlinlang.org/t/calling-blocking-code-in-coroutines/2368/6

这就解释了为什么代码等待睡眠来完成。
这也意味着,这是不可能使用withTimeout上协同程序,阻止线程添加超时。
当使用非阻塞库返回期货withTimeout可以用作这里概述:

为了正确地取消整合,CompletableFuture.await()使用未来的所有组合子做同样的约定 - 它取消了底层今后如果AWAIT称自己被取消。

https://medium.com/@elizarov/futures-cancellation-and-coroutines-b5ce9c3ede3a

从文档的例子旁注:通过添加日志报表延迟/超时例子中,我发现,只有1300米利斯传球所以耽误工作完全符合withTimeout。

08:02:24.736 [主@协程#1] INFO btccCoroutineControllerTest - 我睡觉0 ...
08:02:25.242 [主@协程#1] INFO btccCoroutineControllerTest - 我睡觉1 ...
08:02:25.742 [主@协程#1] INFO btccCoroutineControllerTest - 我睡2 ...
08:02:26.041 [主@协程#1] INFO btccCoroutineControllerTest - 取消



Answer 2:

如果你推出一个孩子协程,等待它完成就可以实现超时后的进展:

fun timeout() {
    runBlocking {
        logger.info("launching")
        try {
            withTimeout(100) {
                execute()
            }
        } catch (e: TimeoutCancellationException) {
            logger.info("timed out", e)
        }
    }
}

private suspend fun execute() =
    GlobalScope.launch(Dispatchers.IO) {
        logger.info("sleeping")
        Thread.sleep(2000)
    }.join()

有了这个,你已经脱钩从您的调度员被阻止孩子协程join()它,所以suspend fun join()可立即消除反应。

请注意,这是不是一个完整的解决方案的更多的一种解决方法,因为在一个线程的IO调度程序将仍然保持阻塞,直到sleep()到期。



文章来源: kotlin coroutine withTimeout does not cancel when using withContext to get non-blocking code