协程:main() runBlocking 与 suspend main

Coroutines: main() runBlocking vs suspend main

取下面两个代码示例(分别取自 Kotlin 文档和协程库 README):

import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(2000)
        println("World")
    }
    println("Hello")
}

在 Kotlin >= 1.3.0 中,可以将 main() 函数标记为 suspendable 并直接使用 coroutineScope

import kotlinx.coroutines.*

suspend fun main() = coroutineScope {
    launch {
        delay(2000)
        println("World")
    }
    println("Hello")
}

两者产生相同的输出:

Hello
World

这两种方法在功能上有什么区别吗?如果是,它们是什么?

runBlocking()coroutineScope() 在功能上非常相似。它们都暂停当前执行,等待内部协程完成。不同之处在于 runBlocking() 是从常规 non-suspendable 代码执行的,因此它通过阻塞等待,而 coroutineScope() 从可挂起的上下文中使用,因此它挂起。

这种差异使他们在内部做很多不同的事情。 runBlocking() 必须先初始化整个协程机制,然后才能执行可暂停的 lambda。 coroutineScope() 可以访问现有的协程上下文,因此它只安排要执行的内部 lambda 并挂起。

现在,suspend fun main() 只是 Kotlin 编译器提供的一个“快捷方式”,可以更轻松地初始化协程应用程序。在内部,它在真正的主函数和可挂起的主函数之间建立了一座桥梁。它生成一个单独的 main() 函数,该函数不可挂起,是“真正的”主函数。此函数初始化协程并使用内部 runSuspend() 实用程序来执行您的 suspend fun main()。此处对此进行了描述:https://github.com/Kotlin/KEEP/blob/master/proposals/enhancing-main-convention.md#implementation-details-on-jvm-1

这两种启动协程应用的方式非常相似,大家可以根据自己的喜好选择。一个显着的区别是 runBlocking() 使用当前的“主”线程创建了一个调度程序。 suspend fun main() 也使用主线程执行,但它没有指定调度程序,所以每当你使用例如coroutineScope() 它将切换到 Dispatchers.Default。因此,您的 runBlocking() 示例使用 single-threaded 调度程序,而您的 coroutineScope() 示例使用 multi-threaded Dispatchers.Default。但是,在两种方法之间进行选择时,实际上不应考虑这种差异。我们可以非常轻松地随时切换调度程序。