首页 > 解决方案 > Kotlin coruntines 在启动和回调中不会执行

问题描述

在拿到这段代码之前,我以为我对 Kotlin 的协程已经足够熟悉了。

1 到 8 全部打印,除了 2:

import kotlinx.coroutines.*
import java.lang.Runnable
import java.lang.Thread.sleep
import kotlin.concurrent.thread

fun main() {
    runBlocking {
        Client.createAccount()
        delay(1000)
    }
}

object Client: CoroutineScope {
    override val coroutineContext = newSingleThreadContext("Client")

    fun createAccount() = launch {
        Client2.init(Runnable {
            println('1')
            launch {
                println('2')
            }
            ok()
            ok2()
        })

        println('7')
        launch {
            println('8')
        }
    }

    fun ok() {
        println('3')
        launch {
            println('4')
        }
    }

    fun ok2() = launch {
        println('5')
        launch {
            println('6')
        }
    }
}

object Client2 {

    fun init(runnable: Runnable) = thread {
        sleep(100)
        runnable.run()
    }
}

结果是:

7
8
1
3
4
5
6

回调中的协程永远不会被调用。为什么?如果我删除1 到 8launchcreateAccount()的将全部打印出来。此外,如果我使用GlobalScope.launch { println('2') }而不是launch { println('2') },我也可以打印 2。

标签: kotlinkotlin-coroutines

解决方案


原因是匿名类将其包装器范围用作父级。

launch { println('2') }inRunnable { }将在父作业createAccount()启动完成时取消。

因此,它不能被调用,因为它会在launch { println('8') }.

所以,如果你Client像下面这样改变,它会正确打印“2”。

object Client: CoroutineScope {
    override val coroutineContext = Dispatchers.Main

    fun createAccount() = launch {
        Client2.init(Run())

        println("7")
        launch {
            println("8")
        }
    }

    fun ok() {
        println("3")
        launch {
            println("4")
        }
    }

    fun ok2() = launch {
        println("5")
        launch {
            println("6")
        }
    }

    class Run: Runnable {
        override fun run() {
            println("1")
            launch {
                println("2")
            }
            ok()
            ok2()
        }
    }
}

推荐阅读