在 Linux 上的 Kotlin/Native 中使用作用域协程调用 class 方法会抛出异常

Calling a class method with scoped coroutine in Kotlin/Native on Linux throws exception

我在 Linux 上有此代码,使用 Kotlin/Native:

//
// This class could be in common module or in 'linuxMain' source set,
// either way doesn't work
//
class MyClass : CoroutineScope by MainScope() {
  fun execute() = launch {
    delay(1000)
    println("finished")
  }
}

fun main() = runBlocking<Unit> {
  val clazz = MyClass()
  clazz.execute()
}

这会引发异常:

kotlin.IllegalStateException: There is no event loop. 
  Use runBlocking { ... } to start one.

我做错了什么?

MainScope() 创建一个使用 Dispatchers.Main 调度程序的作用域。此调度程序在 UI 线程(在 Android、Swing 或 JavaFX 应用程序中)中启动协程。从它的 documentation:

Access to this property may throw an IllegalStateException if no main dispatchers are present in the classpath.

...

In order to work with the Main dispatcher, the following artifact should be added to the project runtime dependencies:

kotlinx-coroutines-android — for Android Main thread dispatcher
kotlinx-coroutines-javafx — for JavaFx Application thread dispatcher
kotlinx-coroutines-swing — for Swing EDT dispatcher

这就是为什么它适用于 Android 而不适用于 Linux。 linuxMain 类路径中没有 UI 库,因此 Dispatchers.Main 抛出异常 (尽管文档说它应该等同于 Dispatchers.Default for Kotlin/Native,我真的不知道为什么它在你的情况下不起作用,请确保你正确构建你的项目)

有一种方法可以修复它,但它需要更改 MyClass 并涉及一些解决方法:

fun getMyScope() = try {
    runBlocking {
        launch(Dispatchers.Main) {}
    }
    MainScope()
} catch(ex: IllegalStateException) {
    CoroutineScope(Dispatchers.Default)
}

class MyClass : CoroutineScope by getMyScope() {
  fun execute() = launch(Dispatchers.Default) {
    delay(1000)
    println("finished")
  }
}

fun main() = runBlocking<Unit> {
  val clazz = MyClass()
  clazz.execute().join()
}

此代码将为您需要在 UI 线程中工作的平台使用主调度程序,并在其他平台上使用默认调度程序。如果你根本不需要使用主调度器,这段代码可以简化:

class MyClass : CoroutineScope by CoroutineScope(Dispatchers.Default) {
  fun execute() = launch {
    delay(1000)
    println("finished")
  }
}

fun main() = runBlocking<Unit> {
  val clazz = MyClass()
  clazz.execute().join()
}