Kotlin 异步不并行运行任务

标签 kotlin kotlin-coroutines kotlinx.coroutines.flow

我正在使用测试文件尝试 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工作感兴趣的范围上下文是IODefaultUnconfined,但我认为在您的情况下,Default 最适用。请记住,单元测试通常需要启动一个阻塞上下文,例如 runBlockingrunTest,因此只有在以下情况下才可以使用 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 协程中的 contextscope,下面的示例是最正确的实现方式,它尊重协程中非常重要的一个概念,即“结构化并发”。这是一个理解起来有点复杂的概念,但一般来说,我们应该在不必要时避免上下文切换:

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

相关文章:

android - 使用 Kotlin Coroutines 和 Flow 测试 Room DAO 的方法

spring-boot - Kotlin 多平台中的 Spring Boot Gradle 依赖项

java - Android Studio 无法解析在 IDE 中用 Kotlin (.kt) 编写的类,但可以正常编译

android - Hilt 和 WorkManager 错误 : lateinit property WorkerFactory has not been initialized

android - 如何以 MVVM 模式与小部件中的数据库进行交互

kotlin - 仅当第一个发出(非空)值时如何组合 Kotlin 流,如果不是,则取消订阅第二个流?

android - 每天在Kotlin,Android Studio中将值重置为0吗?

kotlin - 按顺序运行 Kotlin 协程

kotlin - 如何正确加入 CoroutineScope 中启动的所有作业

android - 挂起函数只能在协程体内调用