如何启动不阻止包含函数返回的协程?

How can I launch coroutine that doesn't hold the containing function from returning?

我有一个函数应该完成它的工作,但也会触发后台进程。触发的进程,即使在函数内启动,也不应阻止它返回,因为它与正在完成的工作没有直接关系。

代码示例:

suspend fun coroutiny(): String {
 coroutineScope {
    launch(Dispatchers.IO) {
        delay(1000)
        println("Independent thing that should not avoid coroutiny to return")
     }
 }
 return "Coroutiny return"
}


suspend fun main() {
 println(coroutiny())
 println("after coroutiny")
 delay(2000)
}

在这种情况下我想要的结果是:

Coroutiny return
after coroutiny
Independent thing that should not avoid coroutiny to return

但我得到的是:

Independent thing that should not avoid coroutiny to return
Coroutiny return
after coroutiny

我知道发生这种情况是因为 coroutineScope {...} 只有 returns 当协程在 return/complete 内时。 我需要帮助的是知道如何在不求助于 GlobalScope.launch {...} 的情况下做我想做的事,我想避免这种情况,因为我的用例与记录的 GlobalScope 可接受用例不匹配。

如您所述,coroutineScope 的方法旨在并行化挂起函数内的工作,但是一旦您 return.

,所有工作都应该完成

如果你想启动比你的函数范围更长的协程,你需要使用一些 CoroutineScope 来启动协程。

获取范围的常用方法是使您的函数成为 CoroutineScope 上的扩展。然后调用者有责任为您的函数提供协程范围(就像设计 launchasync 一样)。此外,通常这些函数是非挂起的(因为挂起函数预计会在 returning 之前完成所有工作)。

fun CoroutineScope.coroutiny(): String {
    launch(Dispatchers.IO) {
        delay(1000)
        println("Independent thing that should not avoid coroutiny to return")
    }
    return "Coroutiny return"
}


fun main() = runBlocking {
    println(coroutiny())
    println("after coroutiny")
    delay(2000)
}

输出(try it yourself):

Coroutiny return
after coroutiny
Independent thing that should not avoid coroutiny to return

如果您的函数存在于具有生命周期的组件中,并且如果将已启动协程的生命周期与该生命周期联系起来是有意义的,那么另一种选择是创建一个 CoroutineScope 作为 属性 该组件,并在组件生命周期结束时适当地取消它。

class MyClassWithLifecycle {

    private val scope = CoroutineScope(CoroutineName("my-custom-processing"))

    fun coroutiny(): String {
        scope.launch(Dispatchers.IO) {
            delay(1000)
            println("Independent thing that should not avoid coroutiny to return")
        }
        return "Coroutiny return"
    }


    fun someCloseOrDisposeFunction() {
        scope.cancel()
    }
}

如果您使用 Android,Kotlin 扩展库会在最重要的生命周期组件中提供开箱即用的范围,例如 lifecycleScopeviewModelScope

这里您将需要另一个比原来的 coroutineScope 寿命更长的示波器。

val myCustomScope = CoroutineScope(Job())

suspend fun coroutiny(): String {
    myCustomScope.launch(Dispatchers.IO) {
        delay(1000)
        println("Independent thing that should not avoid coroutiny to return")
 }
 return "Coroutiny return"
}

suspend fun main() {
    println(coroutiny())
    println("after coroutiny")
    delay(2000)
    myCustomScope.cancel() // Cancel the scope at the right time
}

测试一下here

确保在不再需要时取消 myCustomScope

您要的是GlobalScope.launch

不要发明另一种方法来做完全相同的事情。

如果将 GlobalScope 用于您想要的东西有问题,那么问题在于想要这样做,而不是使用 GlobalScope

特别是,在建议不要使用 GlobalScope 时,Kotlin 团队建议您应该管理您正在启动的所有这些协程的生命周期。至少你应该确保未完成的协程不会无限制地累积。如果调用者的 coroutineScope 没有正确封装协程的生命周期,那么你应该有一个不同的范围 does,并将其作为参数传递。

在我得到的答案的帮助下,这是实现我真正想要的东西的最佳方式:

package com.bmw.otd.validation

import kotlinx.coroutines.*

fun CoroutineScope.coroutiny(): String {
    scope.launch(Dispatchers.IO) {
        delay(1000)
        println("Independent thing that should not avoid coroutiny to return")
    }

    return "Coroutiny return"
}


suspend fun main() = coroutineScope {
    println(coroutiny(this))
    println("after coroutiny")
}

通过这样做,我保证:

  • 函数不等待启动的协程完成 return.
  • 应用程序只有在后台工作完成后才能完成,这在我的测试中无法通过新的“嵌套范围”来保证。