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

前言

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

相似点

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

都用于启动协程

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

都可以返回 Job 对象

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

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

async

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

都支持协程的取消

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

launch cancel

suspend fun testLaunchCancel() {
    val job = GlobalScope.launch {
        log(delay(200))
        log("launch--1")
    }
    job.cancel()
    log("cancel")
}

/**
 * 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 带返回值:

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

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

/**
 * 并发获取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

/**
 * 模拟客户端请求
 */
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的场景:

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 中发生异常,它会被协程框架捕获并记录,但不会抛出到调用方。这意味着您需要明确捕获并处理异常。
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 方法时抛出。

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值