我正在使用测试文件尝试 Kotlin 协程。我观察到 async{..} block 完成,然后下一个异步 block 开始。
@Test
fun `xyz`() {
runBlocking {
val x = async {
x()
}
val y = async {
y()
}
val z = async {
z()
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
}
}
fun x() = "x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
fun y() = "y".also { println("returning y") }
fun z() = "z".also { println("returning z") }
在此代码块中,我可以看到“返回 y”和“返回 z”仅在对 x() 的调用完成后才会打印。这不是我期望 Kotlin 并行运行的方式。您能否告诉我需要进行哪些更改才能同时运行这三个调用,或者不等待一个调用完成。 PS - 测试方法中的代码也可以直接在 main 上运行。 :)
最佳答案
这是一个有趣的问题,您没有获得预期异步行为的原因来自您的 runBlocking 函数及其作为上下文创建的 BlockingEventLoop 。当使用此调度程序运行协程时,您将仅在单个Thread
中工作。尽管从技术上讲,async
异步启动协程,但对于您的示例,您只会在执行诸如 delay
之类的非阻塞操作时看到此行为,而这只是因为 delay
code> 时间表但这当然不是您正在寻找的。您可能对async
工作感兴趣的范围上下文是IO
、Default
或Unconfined
,但我认为在您的情况下,Default
最适用。请记住,单元测试通常需要启动一个阻塞上下文,例如 runBlocking
或 runTest
,因此只有在以下情况下才可以使用 coroutineScope
:已经在协程作用域中(即在 suspend
函数中或在以 runBlocking
开头的作用域中)并且上下文已经是非阻塞的。以下是您自己的代码的 3 个不同版本,以及 3 个不同的示例:一个是阻塞式的,另一个是具有 2 种不同样式的非阻塞式的。
@Test
fun `should xyz synchronously in spite of async`() {
runBlocking {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
}
@Test
fun `should xyz asynchronously because of non-blocking with explicit Dispatchers_IO`() {
runBlocking {
withContext(Dispatchers.Default) {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
}
}
@Test
fun `should xyz asynchronously because of non-blocking with coroutineScope`():Unit = runBlocking {
coroutineScope {
CoroutineScope(Dispatchers.Default).launch {
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}.join()
}
}
请注意,正如 @Joffrey 所建议的,尽管这些示例用于明确说明 Kotlin
协程中的 context
和 scope
,下面的示例是最正确的实现方式,它尊重协程中非常重要的一个概念,即“结构化并发”。这是一个理解起来有点复杂的概念,但一般来说,我们应该在不必要时避免上下文切换:
@Test
fun `should xyz asynchronously because of non-blocking with explicit Dispatchers_IO`() = runBlocking(Dispatchers.Default){
val x = async {
"x".also {
repeat(1000) {
println("waiting for network response.")
}
println("returning x")
}
}
val y = async {
"y".also { println("returning y") }
}
val z = async {
"z".also { println("returning z") }
}
println("x, y, z -> ${x.await()} , ${y.await()}, ${z.await()} ")
println(coroutineContext)
}
还要确保考虑在这些测试中使用断言。它们并不完整。他们只专注于回答您的问题。
关于Kotlin 异步不并行运行任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77823930/