kotlin 在挂起的函数中调用协同例程构建器(启动、异步)时,coroutineContext不明确

f0brbegy  于 2023-01-17  发布在  Kotlin
关注(0)|答案(4)|浏览(150)

我有一个类Runner,它实现了CoroutineScope接口,如下所示。它有一个被挂起的函数run。当我在这个被挂起的run函数中使用协同例程构建器函数(launchasync)时,我收到以下警告
由于suspend函数的CoroutineScope接收器而导致不明确的coroutineContext
Runner类实现了一个coroutineContext属性,有人能解释一下警告消息背后的逻辑吗?

class Runner: CoroutineScope {

    override private val coroutineContext = Dispatchers.IO

    suspend fun run()  {
           val job1 = launch { delay(2000); println("launching job1") }
           val job2 = launch { delay(2000); println("launching job2") }
           listOf(job1, job2).forEach { it.join() }
    }

}
s3fp2yjn

s3fp2yjn1#

由于suspend修饰符,它是不明确的。您的run()函数将从另一个courroutineScope调用。因此,它内部的launch构建器可以启动一个协程或在现有协程内挂起。这就是不明确之处。您可以通过删除suspend修饰符来修复它:

class Runner : CoroutineScope {

    override val coroutineContext = Dispatchers.IO

    fun run() = launch {
            val job1 = launch { delay(2000); println("launching job1") }
            val job2 = launch { delay(2000); println("launching job2") }
            listOf(job1, job2).forEach { it.join() }
        }

}
zmeyuzjn

zmeyuzjn2#

您需要明确声明run()使用Runner的coroutineContext,这将消除歧义

suspend fun run() = withContext(coroutineContext) {
    val job1 = launch { delay(2000); println("launching job1") }
    val job2 = launch { delay(2000); println("launching job2") }
    listOf(job1, job2).forEach { it.join() }
}

或者,如果添加了一个参数,则可以使用调用run的上下文,例如

suspend fun run(cc:CoroutineContext = coroutineContext) = withContext(cc) {
    val job1 = launch { delay(2000); println("launching job1") }
    val job2 = launch { delay(2000); println("launching job2") }
    listOf(job1, job2).forEach { it.join() }
}
bybem2ql

bybem2ql3#

尽量避免在协程作用域类或函数中挂起函数,除非它会抛出上述警告

class Runner: CoroutineScope {

            override private val coroutineContext = Dispatchers.IO

            suspend fun run()  {
                   val job1 = launch { delay(2000); println("launching job1") }
                   val job2 = launch { delay(2000); println("launching job2") }
                   listOf(job1, job2).forEach { it.join() }
            }

        }

class Runner: CoroutineScope {
            //also remove the `private` visibility modifier
            override val coroutineContext = Dispatchers.IO

            fun run()  {
                   val job1 = launch { delay(2000); println("launching job1") }
                   val job2 = launch { delay(2000); println("launching job2") }
                   this.launch{
                       listOf(job1, job2).forEach { it.join() }
                   }   
            }

        }
9bfwbjaz

9bfwbjaz4#

我相信实现您所要做的事情的正确方法是使用corotineScope函数,而不是实现CoroutineScope

class Runner {
    suspend fun run() {
        coroutineScope {
           val job1 = launch { delay(2000); println("launching job1") }
           val job2 = launch { delay(2000); println("launching job2") }
           listOf(job1, job2).forEach { it.join() }
        }
    }
}

如果您真的想使用IO分配器(在这个特定的示例中似乎没有必要),可以改用withContext

suspend fun run() {
    withContext(Dispatchers.IO) {
        ...

原始代码和大多数答案的代码可能会以意想不到的方式运行。使用具有调度程序但没有作业的上下文实现协同程序作用域将导致其行为类似于GlobalScope,由于某种原因,它被标记为一个微妙的API,破坏了结构化并发。

相关问题