在 Kotlin 協程中,launchasyncsuspend 函數和調度器(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

指定協程運行的線程池

-

控制任務執行的線程環境

七、注意事項

  1. runBlocking 的使用:僅用於測試或 main 函數,會阻塞當前線程,生產環境(如 Android)中避免使用。
  2. await() 的位置async 的 await() 應在需要結果時調用,過早調用會導致串行執行(失去並行優勢)。
  3. 取消協程:通過 Job.cancel() 取消協程,需確保 suspend 函數能響應取消(如 delay 是可取消的)。
  4. Android 中的主線程Dispatchers.Main 需要依賴 androidx.lifecycle:lifecycle-viewmodel-ktx 等庫。

通過合理組合這些組件,可以高效實現異步、非阻塞的併發邏輯,避免傳統線程模型的性能開銷