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

相关推荐
居然是阿宋3 小时前
Kotlin 集合函数:map 和 first 的使用场景
kotlin
tangweiguo030519874 小时前
(kotlin) Android 13 高版本 图片选择、显示与裁剪功能实现
android·开发语言·kotlin
每次的天空7 小时前
Android学习总结之Kotlin 协程
android·开发语言·kotlin
stevenzqzq7 小时前
kotlin函数类型
android·开发语言·kotlin
QING61814 小时前
Kotlin commonPrefixWith用法及代码示例
android·kotlin·源码阅读
QING61814 小时前
Kotlin groupByTo用法及代码示例
android·kotlin·源码阅读
兰琛20 小时前
Compose组件转换XML布局
android·xml·kotlin
南宫生1 天前
Java迭代器【设计模式之迭代器模式】
java·学习·设计模式·kotlin·迭代器模式
wangz761 天前
kotlin,jetpack compose,使用DataStore保存数据,让程序下次启动时自动获取
android·kotlin·datastore·jetpack compose
tangweiguo030519871 天前
打破界限:Android XML与Jetpack Compose深度互操作指南
android·kotlin·compose