Kotlin中的协程任务启动:launch 与 async 的差异分析

前言

Kotlin 协程是一种用于编写异步和并发代码的强大工具,它简化了异步任务的处理和管理。在 Kotlin 协程中,launchasync 是两个用于创建并发任务的关键构建块。尽管它们都用于启动协程,但它们在用法上有一些区别。

相似点

首先,让我们来看看 launchasync 之间的相似之处:

都用于启动协程

launchasync 都用于启动一个新的协程,让它在后台执行某些任务。

都可以返回 Job 对象

无论您是使用 launch 还是 async,都会返回一个 Job 对象,它代表了协程的生命周期和状态。这允许您跟踪和管理协程。 代码示例: launch

kotlin 复制代码
suspend fun testLaunch1() {
    val job = GlobalScope.launch {
        log(delay(200))
        log("launch--1")
    }
    job.join()
}

async

kotlin 复制代码
suspend fun testAsync1() {
    val job = GlobalScope.async {
        delay(200)
        log("async--1")
        "jjj"
    }
    job.join()
   // log("result-->${job.await()}")
}

都支持协程的取消

无论您是使用 launch 还是 async,您都可以使用返回的 Job 对象来取消正在运行的协程,以便在需要时停止它们的执行。

launch cancel

kotlin 复制代码
suspend fun testLaunchCancel() {
    val job = GlobalScope.launch {
        log(delay(200))
        log("launch--1")
    }
    job.cancel()
    log("cancel")
}
kotlin 复制代码
/**
 * async cancel
 */
suspend fun testAsyncCancel() {
    val job = GlobalScope.async {
        delay(200)
        log("async--1")
        "jjj"
    }
    job.cancel()
    log("cancel")
}

不同点

尽管 launchasync 有一些相似之处,但它们在以下方面有着明显的不同:

返回值

  • launch 不返回任何结果,它只是启动一个协程并在后台执行任务。通常情况下,launch 用于执行不需要返回值的任务,例如在后台执行一些操作而不需要等待结果。
  • async 返回一个 Deferred 对象,该对象包装了异步计算的结果。您可以使用 await 函数来获取这个结果。因此,async 用于执行需要返回值的任务,您可以等待它完成并获取其结果。

async 带返回值:

kotlin 复制代码
/**
 * async 带返回值
 */
suspend fun testAsync1() {
    val deferred = GlobalScope.async {
        delay(200)
        log("async--1")
        "jjj"
    }
    log("result-->${deferred.await()}")
}

由于async 带返回值,常用于以下并发场景:

kotlin 复制代码
/**
 * 并发获取user
 */
suspend fun testConcurrentAsync() {
    val userList = listOf(1, 2, 3, 4, 5)
    runBlocking {
        val costTime = measureTimeMillis {
            val deferred = userList.map {
                async {
                    ClientManager.getUserAsync(it)
                }
            }
            val result = deferred.awaitAll()
            log("result -->${result.toString()}")
        }
        log("costTime-->${costTime}")

    }
}

其中 ClientManager

kotlin 复制代码
/**
 * 模拟客户端请求
 */
object ClientManager {

    var executor: Executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2)
    val customDispatchers = executor.asCoroutineDispatcher()

    /**
     * getUser
     */
     fun getUser(userId: Int, callback: (User) -> Unit) {
        executor.execute {
            val sleepTime = Random().nextInt(500)
            Thread.sleep(sleepTime.toLong())
            callback(User(userId, sleepTime.toString(), "$userId-->avatar", ""))
        }
    }

    /**
     * getAvatar
     */
    fun getUserAvatar(user: User, callback: (User) -> Unit) {
        executor.execute {
            val sleepTime = Random().nextInt(1000)
            try {
                Thread.sleep(sleepTime.toLong())
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            user.file = "$sleepTime.png"
            callback(user)
        }
    }

    /**
     * 异步同步化
     */
    suspend fun getUserAsync(userId: Int): User = suspendCoroutine { continuation ->
        getUser(userId) {
            continuation.resume(it)
        }
    }
}

相比于java 版本的并发Kotlin async没有加锁的过程,下面是用CountDownLatch并发获取User的场景:

kotlin 复制代码
fun main() {
    val userList = listOf(1, 2, 3, 4, 5)
    val timeCost = measureTimeMillis {
        val countDownLatch = CountDownLatch(userList.size)
        userList.forEach {
            ClientManager.getUser(it) {
                countDownLatch.countDown()
            }
        }
        countDownLatch.await()
    }

    log("timeCost-->$timeCost")
    
}

异常处理

  • launch 不会传播异常给调用者。如果在 launch 中发生异常,它会被协程框架捕获并记录,但不会抛出到调用方。这意味着您需要明确捕获并处理异常。
kotlin 复制代码
suspend fun testError() {
    val job = GlobalScope.launch {
        try {
            getUserError()
        } catch (e: Exception) {
            log("error->${e.message}")
        }
    }
    job.join()
    
}
  • async 会传播异常给调用者。如果在 async 中发生异常,您需要使用 await 函数来获取结果,这可能会抛出已发生的异常。这使得异步任务中的异常更容易被捕获和处理。

总结

  • launch 和 async 都是Kotlin 协程中的关键构建块,用于并发任务。它们的主要区别在于返回值和用途。
  • launch 用于无返回值的任务,而async 用于需要返回值的任务。
  • 异常区别在于异常的处理时机和返回结果的方式。在 launch 中,异常会立即抛出,而在 async 中,异常会被延迟到调用 await 方法时抛出。

源码:github.com/ThirdPrince...

相关推荐
Kapaseker15 小时前
详解 Compose background 的重组陷阱
android·kotlin
黄林晴16 小时前
Kotlin 2.3.20-RC2 来了!JPA 开发者狂喜,6 大更新一文速览
android·kotlin
糖猫猫cc1 天前
Kite:填充处理器
kotlin·orm·kite
Kapaseker2 天前
一杯美式深入理解 data class
android·kotlin
alexhilton4 天前
端侧RAG实战指南
android·kotlin·android jetpack
Kapaseker5 天前
2026年,我们还该不该学编程?
android·kotlin
Kapaseker6 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
Kapaseker7 天前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish7 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker8 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin