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> |
✅ | 批量并行任务,统一收口 |
✅ 推荐实践:
- 始终在
coroutineScope
或supervisorScope
中管理协程,确保结构化并发。 - 需要结果时优先用
async + await
,避免阻塞。 - 处理批量任务时,
map { async { } }.awaitAll()
是经典模式,简洁高效。