似乎我可以使用多种机制来启动协程程序.其中两个是coroutineScopelaunch,但阅读Kotlin Coroutine文档时,我不清楚其中的区别是什么,也不清楚什么时候我会使用其中一个

fun main() {
    println("Main block  ${Thread.currentThread().name}")
    runBlocking {
        coroutineScope {
            println("Coroutine scope  ${Thread.currentThread().name}")
        }
        launch {
            println("Launch  ${Thread.currentThread().name}")
        }
    }
}

我运行了上面的程序,可以看到两者都启动了一个新的协程.有什么关系?

推荐答案

CoroutineScope是一种组织性的东西--它允许你将协程组合在一起,以强制执行structured concurrency.基本上,它允许您控制作用域中所有内容的生命周期(包括在其他协程中启动的协程),并处理传播结果和错误以及取消作用域中的所有内容等事情.More info here

所有协程构建器函数(包括launch)都运行inside a CoroutineScope(如果您想这样看的话,可以将其作为接收器运行on it):

fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext, 
    start: CoroutineStart = CoroutineStart.DEFAULT, 
    block: suspend CoroutineScope.() -> Unit
): Job

因此,您示例中的launch调用运行的是inside the current 102,这是由runBlocking builder提供的:

expect fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

看到你通过的Lambda实际上是CoroutineScope.() -> T了吗?它以CoroutineScope作为接收器运行--这就是你所说的launch.你不能在任何地方呼叫它!

因此,当您在您的示例中创建CoroutineScope时,您是在嵌套runBlocking提供的inside这个CoroutineScope.runBlocking不允许它后面的其余代码继续(即,它blocks执行),直到它的CoroutineScope中的所有内容都完成为止.在其中创建您自己的CoroutineScope允许您定义一个您可以控制的协程子集--您可以控制cancel个协程,但是如果还有其他协程在runBlocking作用域中运行,它将一直阻塞,直到它们也完成为止.


就像Louis在另一个答案中所说的,您实际上并没有在您创建的作用域内创建任何协程.代码只是在runBlocking开始运行您传递的代码块的协程中运行.看看这个:

fun main() {
    println("Main")
    runBlocking {
        coroutineScope {
            delay(1000)
            println("I'm in a coroutine scope")
        }
        launch {
            delay(500)
            println("First new coroutine")
        }
        launch {
            delay(50)
            println("Second new coroutine")
        }
    }
}
Main
I'm in a coroutine scope
Second new coroutine
First new coroutine

单个协程程序中的代码是按顺序执行的-因此发生的情况是

  • 你从Main开始
  • 你进入runBlocking街区的协和程序
  • 你创造了一个CoroutineScope,只影响你身上的launch(等)物品
  • 您延迟,然后打印一行
  • 你打到了前launch个,然后创造了一个新的协和程序.这从延迟500毫秒开始,但现在它自己运行--不同的协程运行concurrently
  • 你移到下一行,然后launch是第二条协议线.它也会自己运行,并且会立即延迟50毫秒.现在有3个协同 routine 在运行
  • 您已经完成了块中的代码执行--这个协程已经完成了.However,有两个正在运行的协程也在这个CoroutineScope上启动,所以runBlocking等待它们完成.
  • 延迟最短的第二个协程程序首先打印,然后完成
  • 第一个协程程序最后结束,因为即使它启动得更早,它们也都是独立运行的
  • 现在runBlockingCoroutineScope内部的一切都完成了,它可以完成并允许在main()中执行

如果您在您创建的作用域中创建了一个协程,那么它的工作方式都是一样的--只是您可以对其中的东西进行细粒度的控制,并且您可以专门使用该嵌套作用域中的协程,而不会影响外部作用域中的内容.重要的是一旦内部作用域完成了,它就可以通知外部作用域一切都完成了,诸如此类的事情

Kotlin相关问答推荐

Jetpack Compose Material3和Material2 Slider onValueChangeFinded()的行为不同

带有Spring Boot和Kotline的可嵌入实体

如何在Kotlin中为接受varargs的函数添加带有默认值的新参数?

可以从背景图像中点击图标吗?

在Kotlin中的嵌套when语句中,else块是强制性的吗?

如何在 kotlin 中的数据类中为变量提供多种类型

为什么Kotlin有次构造函数和init块?

Kotlin 协程:flatMapLatest 什么都不发出

如何在 Spring Boot 3 中为内部类提供运行时提示

mutableStateOf 在 Jetpack Compose 中不记住 API 的情况下保持重组后的值

如何在 kotlin 中创建自定义迭代器并添加到现有类?

Kotlin boxed Int 不一样

如何在 Kotlin 中创建一个打开新活动(Android Studio)的按钮?

ObjectAnimator.ofFloat 不能直接在kotlin中取Int作为参数

在Kotlin中使用@Service时引发异常

Kotlin 中更好的回调方法是什么?侦听器与高阶函数

如何在Kotlin中使用Handler和handleMessage?

在android java类中使用Kotlin扩展

目前不支持 Gradle 项目的自动库版本更新.请手动更新您的 build.gradle

为什么 Kotlin 会收到这样的 UndeclaredThrowableException 而不是 ParseException?