在 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 提供了幾種內置調度器:
|
調度器
|
作用
|
適用場景
|
|
|
主線程(Android 中是 UI 線程)
|
更新 UI(需依賴 Android 庫)
|
|
|
IO 線程池(網絡、文件讀寫等)
|
耗時 IO 操作
|
|
|
CPU 密集型任務線程池(默認)
|
計算密集型任務(如數據處理)
|
|
|
不指定線程(在當前線程執行,暫停後恢復到 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()
}
六、關鍵區別總結
|
組件
|
作用
|
返回值
|
適用場景
|
|
|
標記可暫停的函數
|
無(函數自身有返回值)
|
定義耗時操作
|
|
|
啓動無返回值的協程
|
|
後台任務(無結果)
|
|
|
啓動有返回值的協程
|
|
需結果的異步任務
|
|
|
指定協程運行的線程池
|
-
|
控制任務執行的線程環境
|
七、注意事項
runBlocking的使用:僅用於測試或 main 函數,會阻塞當前線程,生產環境(如 Android)中避免使用。await()的位置:async的await()應在需要結果時調用,過早調用會導致串行執行(失去並行優勢)。- 取消協程:通過
Job.cancel()取消協程,需確保 suspend 函數能響應取消(如delay是可取消的)。 - Android 中的主線程:
Dispatchers.Main需要依賴androidx.lifecycle:lifecycle-viewmodel-ktx等庫。
通過合理組合這些組件,可以高效實現異步、非阻塞的併發邏輯,避免傳統線程模型的性能開銷