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

相关推荐
滴水成冰-1 天前
Kotlin-Flow学习笔记
笔记·学习·kotlin
_Shirley2 天前
android.view.InflateException: Binary XML file line #7: Error inflating class
android·xml·java·ide·kotlin·android studio
ChinaDragonDreamer2 天前
Kotlin:1.9.0 的新特性
android·开发语言·kotlin
帅次4 天前
Android Studio:驱动高效开发的全方位智能平台
android·ide·flutter·kotlin·gradle·android studio·android jetpack
深海呐4 天前
Android 用线程池实现一个简单的任务队列(Kotlin)
android·kotlin·线程池·延时任务队列·线程池延时任务
我命由我123455 天前
Kotlin 极简小抄 P2(插值表达式、运算符、选择结构赋值)
android·java·开发语言·后端·kotlin·安卓
宝杰X75 天前
Compose Multiplatform+kotlin Multiplatfrom第三弹
android·开发语言·kotlin
jiet_h6 天前
Kotlin 中的 `flatMap` 方法详解
开发语言·微信·kotlin
jiet_h6 天前
Android Kotlin 中的 `groupBy` 方法详解
android·开发语言·kotlin
zh路西法7 天前
基于Android Studio的行程记录APK开发指南(三)---界面设计及两种方法获取用户位置
android·ide·kotlin·android studio