正确的方法暂停协程直到任务 完成了

问题描述 投票:6回答:2

我最近潜入Kotlin协同程序因为我使用了很多Google的库,所以大部分工作都是在Task类中完成的

目前我正在使用此扩展来暂停协程

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    task.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            continuation.resume(task.result)
        } else {
            continuation.resumeWithException(task.exception!!)
        }
    }
}

但最近我见过这样的用法

suspend fun <T> awaitTask(task: Task<T>): T = suspendCoroutine { continuation ->
    try {
        val result = Tasks.await(task)
        continuation.resume(result)
    } catch (e: Exception) {
        continuation.resumeWithException(e)
    }
}

有什么区别,哪一个是正确的?

UPD:第二个例子不起作用,idk为什么

android kotlin kotlin-coroutines
2个回答
14
投票

传递给suspendCoroutine { ... }的代码块不应该阻止它被调用的线程,允许协程被挂起。这样,实际线程可以用于其他任务。这是一个关键功能,允许Kotlin协程扩展并在单个UI线程上运行多个协同程序。

第一个例子是正确的,因为它调用task.addOnCompleteListener (see docs)(它只是添加一个监听器并立即返回。这就是为什么第一个正常工作的原因。

第二个示例使用Tasks.await(task) (see docs)来阻止它被调用的线程,并且在任务完成之前不会返回,因此它不允许协程正确挂起。


1
投票

您的第二个示例是更传统的阻止功能。

在典型的Java多线程世界中,您使用类似的函数来阻塞线程,直到结果返回。

同样在典型的Java中,有一种称为Reactive programming的新范例,它允许您在结果返回之前避免阻塞线程。您可以提供一个在结果到达时执行的第一类函数,而不是仅仅等待结果。这样,暂停时没有留下线程;相反,只有一个新事件附加到运行长操作的线程上,当操作完成时,该事件将触发。

阻塞:

Thread one:  --* . . . ./---
Thread two:     \------*

反应:

Thread one: --*   (now free)
Thread two:    \-------*---

如果你研究反应式编程,你会发现更好的解释,深入探讨。

回到你的问题,这两种看似相同的方式是Java范式转变的结果。当然,由于我们在这里与Coroutines合作,我们根本不必担心阻塞问题;语言通过暂停而不是阻止来处理它。因此,反应性编程的许多优点在语言层面上得到了解决。

然而,范式仍然有其优点,所以即使它不像其他地方那样严格必要,看到人们继续以这种模式工作也是有道理的。

© www.soinside.com 2019 - 2024. All rights reserved.