【问题标题】:How to dispatch coroutines directly to main thread on the JVM?如何将协程直接分派到 JVM 上的主线程?
【发布时间】:2019-06-09 00:47:32
【问题描述】:

我正在为 jvm 建立一个基于 kotlin 协程的网络框架。 Client 和 Server 类实现 CoroutineScope,coroutinecontext 的覆盖是 Dispatchers.IO,因为我很确定这是用于这种情况的正确 Dispatcher。但是,我希望在主线程上处理读取数据包,或者至少提供该选项。在没有阅读文档的情况下,我使用了 Dispatchers.Main,我现在意识到它是用于 android UI 线程的。有没有我可以用来让协程在主线程上运行的调度程序?如果没有,我将如何制作一个?

我查看了有关如何基于单个线程创建调度程序的 kotlin 文档,但除了创建新线程的 newSingleThreadContext 之外我找不到任何东西。我还发现可以从 java Executor 创建调度程序,但我仍然不确定如何将其限制为已经存在的线程。

class AbstractNetworkComponent : CoroutineScope {
    private val packetProcessor = PacketProcessor()
    private val job = Job()
    override val coroutineContext = job + Dispatchers.IO
}

class PacketProcessor : CoroutineScope {

    private val job = Job()
    override val coroutineContext = job + Dispatchers.Main //Android only!
    private val packetHandlers = mutableMapOf<Opcode, PacketHandlerFunc>()

    fun handlePacket(opcode: Opcode, packet: ReceivablePacket, networker: Writable) {
        launch(coroutineContext) {
            packetHandlers[opcode]?.invoke(packet, networker)
        }
    }
}

因此,由于缺少 android 组件,使用 Dispatchers.Main 我得到了 IllegalStateException。有没有办法创建一个调度程序来阻止主线程直到它完成(就像 runBlocking 一样?)谢谢!

【问题讨论】:

  • 你在什么环境下?,对于你的问题,我怀疑不是Android,是JavaFx??摇摆??
  • 啊,这很重要;这只是一个通用的 java 应用程序。目前我正在运行一个主函数的测试,但目标是让它成为 jvm 的网络 api。这里没有 javafx 或 swing。

标签: multithreading kotlin kotlin-coroutines coroutinescope


【解决方案1】:

runBlocking 正是您所需要的。它创建一个调度程序并将其设置在协程上下文中。您可以使用

访问调度程序
coroutineContext[ContinuationInterceptor] as CoroutineDispatcher

然后你可以将它传递给一个实现CoroutineScope 的对象或者你想用它做的任何其他事情。下面是一些示例代码:

import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlin.coroutines.ContinuationInterceptor

fun main() {
    println("Top-level: current thread is ${Thread.currentThread().name}")
    runBlocking {
        val dispatcher = coroutineContext[ContinuationInterceptor]
                as CoroutineDispatcher
        ScopedObject(dispatcher).launchMe().join()
    }
}

class ScopedObject(dispatcher: CoroutineDispatcher) : CoroutineScope {
    override val coroutineContext = Job() + dispatcher

    fun launchMe() = launch {
        val result = withContext(IO) {
            "amazing"
        }
        println("Launched coroutine: " +
                "current thread is ${Thread.currentThread().name}, " +
                "result is $result")
    }
}

这将打印出来

Top-level: current thread is main
Launched coroutine: current thread is main, result is amazing

【讨论】:

  • 谢谢!奇迹般有效。如果我将 runBlocking 位放在 ScopedObject 中并在那里获取上下文,它会只使用创建对象的线程吗?
  • runBlocking 调度到它被调用的线程。但由于它是创建协程运行环境的低级机制,因此我认为将其放在作为该环境客户端的对象中并不是一个好的设计。
  • 如何在不使用 Dispatchers.Main 的情况下从教程中转换此代码:val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())?我尝试在一个普通的 Kotlin 控制台项目中使用Dispatchers.Main 进行测试,就像这个 OP 一样,我无法避免 Dispatchers.Main 不存在的运行时错误。
  • @DamnVegetables Main 仅针对 GUI 环境定义。如果您只编写顶级示例代码,您可能会使用Default 调度程序,您甚至不必指定它。您可能还想使用runBlocking,它创建自己的调度程序并使用它在其主体中运行协程。
【解决方案2】:

根据Guide to UI programming with coroutineskotlinx.coroutines 具有三个模块,为不同的 UI 应用程序库提供协程上下文:

此外,UI 调度程序可通过 kotlinx-coroutines-coreDispatchers.Main 获得,相应的实现(Android、JavaFx 或 Swing)由 ServiceLoader API 发现。例如,如果您正在编写 JavaFx 应用程序,您可以使用 Dispatchers.MainDispachers.JavaFx 扩展名,这将是同一个对象。

【讨论】:

  • 我看到了那个指南,但是“import kotlinx.coroutines.javafx.JavaFx as Main”说“未解决的参考:javafx”。但是我的依赖项中确实有 kotlin-coroutines core 1.3.3,只是 javafx 部分以某种方式丢失了。
  • 我猜你需要将kotlinx-coroutines-javafx模块添加到依赖项中。
  • 谢谢,我在 search.maven.org 上找到了它,它有效
猜你喜欢
  • 2022-11-27
  • 2020-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
相关资源
最近更新 更多