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...

相关推荐
Kapaseker18 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
FunnySaltyFish1 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
Kapaseker2 天前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
Kapaseker3 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z5 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton5 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream5 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam5 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker6 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
糖猫猫cc6 天前
Kite:两种方式实现动态表名
java·kotlin·orm·kite