故事背景:忙碌的智能厨房
想象一下,你是一位顶级厨师,经营着一个现代化的智能厨房。你有多个助手(协程)可以同时处理不同的烹饪任务,而launch和async就是你指挥助手的两种不同方式。
人物介绍
- 主厨 (你/Main Thread):负责协调和最终呈现
- launch助手:只管做事,不汇报结果
- async助手:做事后会带回结果
- 厨房调度员 (Dispatcher):决定哪个助手在哪个工作台工作
第一幕:launch助手的单飞任务
故事场景
主厨需要准备一道复杂的菜品,其中有些步骤可以同时进行,比如:
- 煮汤(耗时任务)
- 切菜(耗时任务)
- 摆盘(必须在最后完成)
kotlin
// 厨房协程作用域
val kitchenScope = CoroutineScope(Dispatchers.Main)
fun prepareDish() {
kitchenScope.launch {
// 启动第一个助手煮汤(不关心结果)
launch(Dispatchers.IO) {
val soup = makeSoup()
println("汤煮好了: $soup")
}
// 启动第二个助手切菜(不关心结果)
launch(Dispatchers.Default) {
val vegetables = chopVegetables()
println("菜切好了: $vegetables")
}
// 主厨等待所有助手完成后再摆盘
println("等待所有准备工作完成...")
// 这里会自动等待所有launch的任务完成
// 所有准备工作完成后摆盘
arrangePlate()
}
}
suspend fun makeSoup(): String {
delay(3000) // 模拟煮汤3秒
return "美味汤品"
}
suspend fun chopVegetables(): String {
delay(2000) // 模拟切菜2秒
return "新鲜蔬菜"
}
fun arrangePlate() {
println("🎉 菜品摆盘完成!可以上菜了!")
}
launch的工作原理
text
主厨大脑 (主线程)
↓
launch { ... } → 创建新协程
↓
调度员分配工作台 (Dispatcher)
↓
助手在工作台执行任务 (后台线程)
↓
任务完成,自动清理
第二幕:async助手的团队协作
故事场景
主厨需要准备一份套餐,需要精确计算各个部分的准备时间,并在最后组合:
kotlin
suspend fun prepareComboMeal() {
kitchenScope.launch {
println("👨🍳 开始准备豪华套餐...")
// 使用async同时准备三道菜,并需要它们的结果
val mainCourse = async(Dispatchers.IO) {
prepareMainCourse()
}
val sideDish = async(Dispatchers.IO) {
prepareSideDish()
}
val dessert = async(Dispatchers.Default) {
prepareDessert()
}
println("⏰ 所有菜品都在准备中,主厨可以休息一下...")
// 等待所有菜品完成并获取结果
val combo = ComboMeal(
mainCourse = mainCourse.await(), // 挂起等待结果
sideDish = sideDish.await(), // 挂起等待结果
dessert = dessert.await() // 挂起等待结果
)
println("🎊 套餐完成: $combo")
}
}
suspend fun prepareMainCourse(): String {
delay(4000) // 主菜需要4秒
return "🥩 招牌牛排"
}
suspend fun prepareSideDish(): String {
delay(2500) // 配菜需要2.5秒
return "🥗 田园沙拉"
}
suspend fun prepareDessert(): String {
delay(3000) // 甜点需要3秒
return "🍰 巧克力蛋糕"
}
data class ComboMeal(val mainCourse: String, val sideDish: String, val dessert: String)
第三幕:混合使用的实战场景
故事场景:餐厅高峰期
在真正的餐厅中,我们需要同时使用launch和async:
kotlin
class RestaurantManager {
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
// 处理客户订单
fun processOrder(order: Order) {
scope.launch {
// 使用async并行准备需要结果的菜品
val hotDishes = async(Dispatchers.IO) { prepareHotDishes(order) }
val coldDishes = async(Dispatchers.IO) { prepareColdDishes(order) }
val drinks = async(Dispatchers.Default) { prepareDrinks(order) }
// 使用launch处理不需要结果的日志记录
launch(Dispatchers.IO) {
logOrderPreparation(order)
}
// 等待所有菜品完成
val meal = combineDishes(
hotDishes.await(),
coldDishes.await(),
drinks.await()
)
// 上菜
serveMeal(meal)
// 清理工作(不需要结果)
launch(Dispatchers.IO) {
cleanKitchen()
}
}
}
suspend fun prepareHotDishes(order: Order): List<String> {
delay(5000)
return listOf("热汤", "主菜")
}
suspend fun prepareColdDishes(order: Order): List<String> {
delay(3000)
return listOf("沙拉", "前菜")
}
suspend fun prepareDrinks(order: Order): List<String> {
delay(2000)
return listOf("果汁", "红酒")
}
fun cleanKitchen() {
println("🧹 厨房清理完成")
}
}
核心技术原理揭秘
1. launch 的内部结构
kotlin
// 简化版的launch实现原理
fun launch(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> Unit
): Job {
// 创建新的协程
val completion = StandardCoroutine(context)
// 启动协程
completion.start()
return completion
}
2. async 的内部结构
kotlin
// 简化版的async实现原理
fun <T> async(
context: CoroutineContext = EmptyCoroutineContext,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
// 创建可返回结果的协程
val deferred = CompletableDeferred<T>()
val coroutine = AsyncCoroutine(context, deferred)
coroutine.start()
return deferred
}
时序图:完整的烹饪流程
关键区别总结表
特性 | launch | async |
---|---|---|
返回值 | Job (无结果) | Deferred (有结果) |
使用场景 | 后台任务,不关心结果 | 需要获取结果的并行任务 |
错误处理 | 立即抛出异常 | 在await()时抛出异常 |
等待方式 | join() | await() |
比喻 | 单飞的助手 | 带对讲机的助手 |
最佳实践小贴士
-
使用launch当:
- 执行后台任务不需要结果
- 日志记录、数据上报
- 清理工作
-
使用async当:
- 需要并行执行多个任务并收集结果
- 需要任务执行的返回值
- 进行性能优化(并行计算)
-
记住:
kotlin// ❌ 错误:这样不会并行执行 val result1 = async { task1() }.await() val result2 = async { task2() }.await() // ✅ 正确:先启动所有async,再await val deferred1 = async { task1() } val deferred2 = async { task2() } val result1 = deferred1.await() val result2 = deferred2.await()
通过这个厨房故事,你应该能理解launch和async就像是你厨房里的两种不同类型的助手:一种只管做事(launch),另一种做事后还会向你汇报结果(async)。合理使用它们,你的"应用程序厨房"就能高效运转!