在 Kotlin 协程中,launch、async、suspend 函数和调度器(Dispatchers)是核心组件,它们共同实现了轻量级的并发编程。以下是详细解析和使用示例:
一、协程基础概念
协程是一种轻量级线程,由 Kotlin 运行时管理(而非操作系统),可以在单线程内实现多任务切换,避免线程上下文切换的开销。
二、suspend 函数:协程的 "暂停点"
suspend 是用于修饰函数的关键字,标记该函数只能在协程或其他 suspend 函数中调用,且内部可以 "暂停" 执行(不会阻塞线程)。
- 特性 :
- 暂停时会保存当前状态(局部变量、执行位置等),恢复时继续执行。
- 本身不会创建协程,仅标记可暂停的操作(如网络请求、IO 等)。
kotlin
// 定义 suspend 函数(模拟耗时操作)
suspend fun fetchData(): String {
delay(1000) // delay 是 Kotlin 提供的 suspend 函数,模拟暂停(非阻塞)
return "Data from network"
}
- 注意 :
suspend函数不能直接在主线程(非协程)中调用,必须通过协程构建器启动。
三、协程构建器:launch 与 async
用于创建并启动协程,最常用的是 launch 和 async。
1. launch:启动 "无返回值" 的协程
- 作用:启动一个协程,返回
Job对象(用于管理协程生命周期,如取消、等待)。 - 适用场景:执行不需要返回结果的后台任务(如日志打印、数据保存)。
kotlin
import kotlinx.coroutines.*
fun main() = runBlocking { // runBlocking 是顶层函数,用于在非协程环境启动协程(会阻塞当前线程直到内部协程完成)
// 启动一个协程
val job = launch {
println("Coroutine started")
delay(1000) // 暂停 1 秒(非阻塞)
println("Coroutine finished")
}
job.join() // 等待协程执行完成(阻塞当前协程,非线程)
println("Main finished")
}
-
输出: plaintext
Coroutine started Coroutine finished Main finished
2. async:启动 "有返回值" 的协程
- 作用:启动一个协程,返回
Deferred<T>对象(Job的子类),通过await()获取返回值(T类型)。 - 适用场景:需要获取结果的异步任务(如并行请求多个接口)。
kotlin
fun main() = runBlocking {
// 启动带返回值的协程
val deferred = async {
delay(1000)
42 // 返回值
}
val result = deferred.await() // 等待结果(非阻塞,会暂停当前协程)
println("Result: $result") // 输出:Result: 42
}
- 并行执行 :
async适合并行任务,多个async可同时执行,最后用await()汇总结果:
kotlin
fun main() = runBlocking {
val time = measureTimeMillis { // 测量执行时间
val deferred1 = async { fetchData1() }
val deferred2 = async { fetchData2() }
// 并行执行,总时间约为较慢任务的时间(而非两者之和)
val result1 = deferred1.await()
val result2 = deferred2.await()
println("Total: $result1 + $result2 = ${result1 + result2}")
}
println("Time: $time ms") // 约 2000 ms(因 fetchData2 耗时 2 秒)
}
suspend fun fetchData1(): Int {
delay(1000)
return 100
}
suspend fun fetchData2(): Int {
delay(2000)
return 200
}
四、调度器(Dispatchers):指定协程运行的线程
协程需要运行在调度器指定的线程池上,Kotlin 提供了几种内置调度器:
| 调度器 | 作用 | 适用场景 |
|---|---|---|
Dispatchers.Main |
主线程(Android 中是 UI 线程) | 更新 UI(需依赖 Android 库) |
Dispatchers.IO |
IO 线程池(网络、文件读写等) | 耗时 IO 操作 |
Dispatchers.Default |
CPU 密集型任务线程池(默认) | 计算密集型任务(如数据处理) |
Dispatchers.Unconfined |
不指定线程(在当前线程执行,暂停后恢复到 resume 线程) | 特殊场景(谨慎使用) |
使用方式:通过 CoroutineContext 指定
协程构建器(launch/async)可通过参数指定调度器:
kotlin
fun main() = runBlocking {
// 1. IO 调度器(处理网络请求)
launch(Dispatchers.IO) {
println("IO thread: ${Thread.currentThread().name}") // 输出:IO thread: DefaultDispatcher-worker-1
fetchData()
}.join()
// 2. Default 调度器(处理计算任务)
val result = async(Dispatchers.Default) {
println("Default thread: ${Thread.currentThread().name}") // 输出:Default thread: DefaultDispatcher-worker-2
1 + 1
}.await()
println("Result: $result")
}
五、协程上下文(CoroutineContext)
调度器是 CoroutineContext 的一部分,上下文还包括 Job(协程生命周期)、CoroutineName(协程名称,用于调试)等。可通过 + 组合多个上下文元素:
kotlin
fun main() = runBlocking {
launch(Dispatchers.IO + CoroutineName("NetworkCoroutine")) {
println("Name: ${coroutineContext[CoroutineName]}") // 输出:Name: CoroutineName(NetworkCoroutine)
fetchData()
}.join()
}
六、关键区别总结
| 组件 | 作用 | 返回值 | 适用场景 |
|---|---|---|---|
suspend |
标记可暂停的函数 | 无(函数自身有返回值) | 定义耗时操作 |
launch |
启动无返回值的协程 | Job |
后台任务(无结果) |
async |
启动有返回值的协程 | Deferred<T> |
需结果的异步任务 |
Dispatchers |
指定协程运行的线程池 | - | 控制任务执行的线程环境 |
七、注意事项
runBlocking的使用:仅用于测试或 main 函数,会阻塞当前线程,生产环境(如 Android)中避免使用。await()的位置 :async的await()应在需要结果时调用,过早调用会导致串行执行(失去并行优势)。- 取消协程 :通过
Job.cancel()取消协程,需确保 suspend 函数能响应取消(如delay是可取消的)。 - Android 中的主线程 :
Dispatchers.Main需要依赖androidx.lifecycle:lifecycle-viewmodel-ktx等库。
通过合理组合这些组件,可以高效实现异步、非阻塞的并发逻辑,避免传统线程模型的性能开销