在启动和回调过程中,Kotlin coruntines不会执行

时间:2019-12-01 16:23:39

标签: kotlin kotlin-coroutines

我以为我对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

回调中的协程将永远不会被调用。为什么? 如果我删除了launch中的createAccount(),则1至8将全部打印出来。 另外,如果我使用GlobalScope.launch { println('2') }代替launch { println('2') },我也可以打印出2。

2 个答案:

答案 0 :(得分:2)

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

完成启动的父作业launch { println('2') }后,Runnable { }中的

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()
        }
    }
}

答案 1 :(得分:0)

launch在处理程序中发布一个Runnable,因此它的代码执行不是立即的。 launch(Dispatchers.Main,CoroutineStart.UNDISPATCHED)将立即在当前线程中执行其lambda表达式。

将调度程序更改为当前使用的调度程序 将午餐从线程内更改为

launch (coroutineContext, CoroutineStart.UNDISPATCHED)

fun createAccount() = launch {

    Client2.init(Runnable {
        println('1')
        launch (coroutineContext, CoroutineStart.UNDISPATCHED){
            println('2')
        }
        ok()
        ok2()
    })

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

输出:

7
8
1
2
3
4
5
6