1. 概述

Kotlin 协程(coroutines)可以理解为轻量级线程。相比传统线程,它资源消耗更少,能实现高效的并发执行。✅ 使用协程有助于提升应用的性能、响应速度和可扩展性。

本文将介绍在 Kotlin 中并行运行多个协程的常用方法。目标是让你在实际开发中避免“踩坑”,写出更高效、结构清晰的异步代码。

🔗 参考:Kotlin 协程官方基础文档


2. 使用 launch() 并行启动协程

最直接的方式是使用 launch() 启动多个协程,并让它们并行执行。

suspend fun executeTwoCoroutinesInParallelUsingLaunch() {
    coroutineScope {
        launch { doSomething() }
        launch { doSomethingElse() }
        println("Waiting for coroutines to finish")
    }
}

suspend fun doSomething() {
    val delay = Random.nextInt(100, 1000)
    delay(delay.milliseconds)
    println(" - doSomething waited for $delay milliseconds")
}

suspend fun doSomethingElse() {
    val delay = Random.nextInt(100, 1000)
    delay(delay.milliseconds)
    println(" - doSomethingElse waited for $delay milliseconds")
}

关键点解析:

  • coroutineScope {}

    • 创建一个新的协程作用域,在其中执行代码块。
    • 遵循结构化并发(structured concurrency)原则:一旦任一子协程失败,整个作用域都会取消,其他子协程也会被自动清理。
    • ❗只有当代码块及其所有子协程都完成后,coroutineScope 才会返回。
  • launch {}

    • 启动一个不带回调结果的协程,适合“只执行,不关心返回值”的场景。
    • 返回一个 Job 对象,可用于控制协程生命周期(如取消、等待完成等)。
  • doSomething()doSomethingElse()

    • 模拟耗时操作(随机延迟),通过 delay() 非阻塞地挂起协程,不占用线程。

执行耗时测试:

val executionTime = measureTimeMillis {
    executeTwoCoroutinesInParallelUsingLaunch()
}
println(" - The whole example took $executionTime milliseconds")

输出示例(每次运行顺序不同):

Waiting for coroutines to finish
 - doSomething waited for 309 milliseconds
 - doSomethingElse waited for 939 milliseconds
 - The whole example took 953 milliseconds

或:

Waiting for coroutines to finish
 - doSomethingElse waited for 564 milliseconds
 - doSomething waited for 581 milliseconds
 - The whole example took 584 milliseconds

⚠️ 注意:总耗时 ≈ 最长单个任务时间,说明确实是并行执行,而非串行叠加。

🔗 Job 详细生命周期见:Kotlin 官方 Job 文档


3. 使用 async() 并行执行并获取结果

如果需要处理协程的返回值,应使用 async() 而非 launch()

suspend fun executeTwoCoroutinesInParallelUsingAsync() {
    coroutineScope {
        val something = async { fetchSomething() }
        val somethingElse = async { fetchSomethingElse() }

        println("Waiting for coroutines to finish")
        println(
            "The sum of something and somethingElse " +
                "is: ${something.await() + somethingElse.await()}",
        )
    }
}

suspend fun fetchSomething(): Int {
    val delay = Random.nextInt(100, 1000)
    delay(delay.milliseconds)
    println(" - fetchSomething waited for $delay milliseconds")
    return delay
}

suspend fun fetchSomethingElse(): Int {
    val delay = Random.nextInt(100, 1000)
    delay(delay.milliseconds)
    println(" - fetchSomethingElse waited for $delay milliseconds")
    return delay
}

核心机制:

  • async {}

    • 启动一个协程并返回 Deferred<T> 类型对象(类似 Future<T>)。
    • 协程在后台执行,结果可通过 await() 获取。
  • await()

    • 是一个 suspend 函数,不会阻塞线程,而是挂起当前协程直到结果就绪。
    • ⚠️ 必须在协程内部或 suspend 函数中调用。

执行输出:

val executionTime = measureTimeMillis {
    executeTwoCoroutinesInParallelUsingAsync()
}
println(" - The whole example took $executionTime milliseconds")

输出:

Waiting for coroutines to finish
 - fetchSomething waited for 588 milliseconds
 - fetchSomethingElse waited for 678 milliseconds
The sum of something and somethingElse is: 1266
 - The whole example took 697 milliseconds

✅ 总耗时接近两个任务中的最大值,说明并行有效,且成功获取了结果。


4. 并行执行多个协程并统一获取结果

当需要并行处理大量相似任务,并在全部完成后统一处理结果时,推荐使用 awaitAll()

suspend fun executeManyCoroutinesInParallelUsingAsync(): List<Int> {
    val result = coroutineScope {
        (1..5).map { n ->
            async {
                val delay = Random.nextInt(100, 1000)
                delay(delay.milliseconds)
                println("- processing $n")
                n * n
            }
        }.awaitAll()
    }
    println("Result: $result")
    return result
}

输出:

- processing 3
- processing 5
- processing 1
- processing 4
- processing 2
Result: [1, 4, 9, 16, 25]

优势说明:

  • ✅ 所有任务并行执行,效率最大化。
  • awaitAll() 自动等待所有 Deferred 完成,并按原始顺序返回结果列表,无需手动排序。
  • ✅ 代码简洁,避免了对每个 async 手动调用 await()

🔗 awaitAll() 官方文档:Kotlin awaitAll 扩展函数
支持两种形式:集合扩展函数 和 可变参数版本,根据场景选择更清晰的写法。


5. 总结

方法 返回类型 是否带回结果 典型用途
launch {} Job 纯异步任务,如日志记录、事件发送
async {} Deferred<T> 需要结果的并发计算
awaitAll() List<T> 批量并行任务,统一收口

✅ 推荐实践:

  • 始终在 coroutineScopesupervisorScope 中管理协程,确保结构化并发。
  • 需要结果时优先用 async + await,避免阻塞。
  • 处理批量任务时,map { async { } }.awaitAll() 是经典模式,简洁高效。

💡 源码地址:GitHub - Baeldung Kotlin Coroutines Example


原始标题:How to Run Parallel Coroutines in Kotlin