Skip to content

协程基础与挂起本质

源:Kotlin 协程概览

协程 (Coroutines) 是一种基于线程之上的 用户态轻量级线程。 它的核心优势在于:以顺序写法的代码,实现非阻塞的异步逻辑

什么是“轻量级”?

你可以轻松创建 10 万个协程,但这在线程世界是不可想象的(会 OOM)。 协程不绑定到特定线程,它可以在一个线程挂起,并在另一个线程恢复。

kotlin
// 启动 10 万个协程,仅占用极少内存
repeat(100_000) {
    launch {
        delay(1000L)
        print(".")
    }
}

挂起函数 (Suspend Function)

suspend 关键字是协程魔法的开关。

  • 非阻塞挂起: 当协程遇到 delay 或其他挂起函数时,它会释放当前占用的线程(线程去干别的事了),直到挂起结束。
  • 自动恢复: 挂起结束后,协程会被调度器重新分配线程继续执行。
kotlin
suspend fun fetchDoc() {
    // 挂起点 1: 释放主线程,网络请求在 IO 线程跑
    val data = networkRequest() 
    // 自动切回:继续执行
    show(data)
}

协程构建器

只有构建器(Builder)才能从普通世界跨入协程世界。

kotlin
// "Fire-and-Forget" (发射即不管)
// 返回 Job,无返回结果
scope.launch {
    // 异步逻辑
}
kotlin
// "Future-style" (有结果)
// 返回 Deferred<T>,需调用 await() 获取结果
val deferred = scope.async {
    calculate()
}
val result = deferred.await()
kotlin
// 阻塞当前线程直到内部逻辑执行完
// ⚠️ 仅用于 main() 函数。严禁在 UI 线程使用!
// 对于单元测试,请优先使用专用的 [runTest](/kotlin/coroutines-testing)。
runBlocking {
    delay(1000)
}

结构化并发 (Structured Concurrency)

Kotlin 协程不鼓励“野生”协程。所有协程必须依附于一个 作用域 (CoroutineScope)

  1. 父子关系: 在父协程中启动的子协程,自动建立层级关系。
  2. 同生共死: 父作用域取消,所有子协程自动取消。
  3. 责任闭环: 父协程必须等待所有子协程执行完毕,自己才能完成。
kotlin
// 自定义作用域逻辑
suspend fun loadUI() = coroutineScope { // 创建一个子作用域
    launch { loadPart1() }
    launch { loadPart2() }
    // 只有当 part1 和 part2 都结束,loadUI 函数才会返回
}

scope 构建器对比

  • coroutineScope: 一个失败,全家爆炸。任何子协程抛出异常,整个作用域取消。
  • supervisorScope: 各自安好。子协程失败不会影响兄弟协程。
kotlin
supervisorScope {
    launch { throw Error() } // 炸了
    launch { delay(100); print("I am alive") } // 依然活着 ✅
}