我正在试着用Kotlin协程来理解异步代码执行.

为什么在启动异步代码块时必须指定调度程序?

以下代码块

fun doInParallel(): Unit = runBlocking {
    coroutineScope {
        launch { println("start A").also { Thread.sleep(1_000) }.also { println("finish A") } }
        launch { println("start B").also { Thread.sleep(1_000) }.also { println("finish B") } }
    }
}

将始终打印

start A
finish A
start B
finish B

这并不是我所期望的.

仅在显式指定调度程序之后,异步代码块才会并行运行:

fun doInParallel(): Unit = runBlocking {
    coroutineScope {
        launch(Dispatchers.Default) { println("start A").also { Thread.sleep(1_000) }.also { println("finish A") } }
        launch(Dispatchers.Default) { println("start B").also { Thread.sleep(1_000) }.also { println("finish B") } }
    }
}

将打印

start A
start B
finish A
finish B

为什么我必须包括Dispatcher.Default

我用的是org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0-Beta(如果这有关系的话)

推荐答案

使用delay(...)而不是Thread.sleep(...).

Thread.sleep阻塞线程,使用协程的 idea 是您希望suspend线程,而不是阻塞它.当使用像delay这样的挂起函数从一个协程挂起一个线程时,同一个线程可以自由地服务于另一个协程--在本例中是由第二个launch启动的那个协程.

在第二个示例中指定Dispatcher具有不同行为的原因是,coroutineScope从父作用域继承协程上下文,在本例中为runBlocking.runBlocking中的默认调度程序记录如下:

此构建器的默认CoroutineDispatcher是事件循环的内部实现,它处理此阻塞线程中的延续,直到此协程完成.

换句话说,在您的示例中,所有东西都运行在单个线程中:主线程(或调用doInParallel的任何线程).这与使用阻塞Thread.sleep相结合意味着代码以阻塞方式并且同步地执行.

总而言之:

  1. 更改代码以打印线程名称,您将看到所有内容都在main线程中运行(除非您指定了调度程序).

  2. 从使用Thread.sleep更改为使用delay来修复底层问题,并允许单个线程运行两个启动的协程.

fun doInParallel(): Unit = runBlocking {
  coroutineScope {
    launch { println("${Thread.currentThread().name} start A").also { delay(1_000) }.also { println("${Thread.currentThread().name} finish A") } }
    launch { println("${Thread.currentThread().name} start B").also { delay(1_000) }.also { println("${Thread.currentThread().name} finish B") } }
  }
}

Kotlin相关问答推荐

用浮点数或十进制数给出错误答案的阶乘计算

为什么Kotlin函数参数名会 destruct 方法调用?

S使用MAP和ElseThrow的习惯用法是什么?

用于将 0.5 变为 0 的 round() 函数的模拟

按钮无法在 Android Studio 上打开新活动

Kotlin:我可以将函数分配给 main 的伴随对象中的变量吗?

如何从 var list 或可变列表中获取列表流

如何使用子变量在 Kotlin 中初始化父级

为 Gradle 子项目配置 Kotlin 扩展

异常传播如何在 CoroutineScope.async 上工作?

Android 导航组件 - 向上导航打开相同的片段

有没有办法重用 Job 实例?

如何修复 ViewPager2 中的Design assumption violated错误?

变量后的Android问号

runOnUiThread 没有调用

Firestore - 如何在 Kotlin 中排除数据类对象的字段

如何暂停kotlin coroutine直到收到通知

如何在Kotlin中创建无限长的序列

以Kotlin为单位的货币数据类型

Kotlin:使用自定义设置器时没有lateinit的解决方法?