Kotlin协程超时处理

14

我正在编写一个测试函数,它应该运行一个代码块,或者(当达到某个特定的超时时间)抛出异常。

我曾尝试使用 Kotlin 中的 Coroutines,但最终得到了一种混合使用 CoroutinesCompletableFuture 的结果:

fun <T> runBlockWithTimeout(maxTimeout: Long, block: () -> T ): T {
    val future = CompletableFuture<T>()

    // runs the coroutine
    launch { block() }

    return future.get(maxTimeout, TimeUnit.MILLISECONDS)
}

这个方法可行,但我不确定这是否是在Kotlin中解决该问题的预期方式。

我还尝试了其他方法:

runBlocking {
    withTimeout(maxTimeout) {
        block()
    }
}

但是当block调用例如Thread.sleep(...)时,这似乎不起作用。

那么,CompletableFuture方法是正确的方法,还是有更好的方法?

更新1 我想要实现的目标:

异步集成测试代码(如从RabbitMq接收数据)应该像这样进行测试:

var rabbitResults: List = ... // are filled async via RabbitListeners
...
waitMax(1000).toSucceed {
    assertThat(rabbitResults).hasSize(1)
}
waitMax(1000).toSucceed {
    assertThat(nextQueue).hasSize(3)
}
...

2
如果您正在使用某些测试库,它可能已经具有超时功能。例如,使用JUnit,您可以在测试方法上设置“@Test(timeout=5000)”注释。 - egor.zhdan
我更新了描述,以便更好地理解我想要实现的内容。 - guenhter
1个回答

49

withTimeout { ... } 的设计是为了在超时时取消正在进行的操作,这只有在相关操作可取消时才可能发生。

它之所以可以与 future.get(timeout, unit) 一起使用,是因为它只是等待超时时间,而不会实际上取消或中止任何后台操作,即使超时已经过去,后台操作仍然会继续执行。

如果你想要使用协程模拟类似的行为,那么你应该使用带有超时时间的 等待 操作,就像这样:

val d = async { block() } // run the block code in background
withTimeout(timeout, unit) { d.await() } // wait with timeout

之所以它能正常工作,是因为await是一个可取消的函数,您可以通过阅读其API文档来验证这一点。

但是,如果您想在超时时实际取消进行中的操作,则应以异步和可取消的方式实现您的代码。 取消是“合作性”的,因此首先,在您的代码中使用的底层库必须提供支持取消进行中操作的异步API。

您可以在协程指南的相应部分以及观看KotlinConf的深入了解协程,了解有关取消和超时的更多信息,以及如何将协程与异步库集成。


Roman,有没有类似于d.await(timeout)的东西,它不会取消,而是从当前挂起的函数中唤醒,给予检查的机会,如果需要,执行d.cancel()...我希望d保持工作状态,直到手动取消。 - vach
使用 withTimeout(timeout) { d.await() } 来实现这个目的 - 它正是这样做的。异步任务在超时后仍然在工作。 - Roman Elizarov
我相信这就是我尝试做的事情,如果您有一分钟时间,请看看我的尝试:https://dev59.com/0bHma4cB1Zd3GeqPKmLJ - vach
这是协程指南中“取消和超时”部分的当前位置:https://kotlinlang.org/docs/cancellation-and-timeouts.html - dugsmith
协程应该是可取消的!!! 这很重要。 - kazimad
可以使用runInterruptible()吗? - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接