带超时的 Kotlin 协程

Kotlin Coroutines with timeout

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

我在 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)
}
...

withTimeout { ... } 旨在 取消 正在进行的超时操作,只有当相关操作 可取消.

它与 future.get(timeout, unit) 一起工作的原因是因为它只 等待 超时。它实际上不会以任何方式取消或中止您的后台操作,该操作在超时后仍继续执行。

如果你想用协同程序模仿类似的行为,那么你应该等待超时,像这样:

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

它工作正常,因为 await 是一个可取消的函数,您可以通过阅读 its API documentation.

来验证

但是,如果你真的想在超时时取消正在进行的操作,那么你应该以异步和可取消的方式实现你的代码。取消是 合作 ,因此,首先,您在代码中使用的基础库必须提供支持取消正在进行的操作的异步 API。

您可以在有关如何将协程与异步库集成的 coroutines guide and watch the KotlinConf's Deep Dive into Coroutines 的相应部分阅读有关取消和超时的更多信息。