在主线程中运行非阻塞协同程序

我们有一个特殊的用例,如果我们可以用Kotlin的协程来解决我们的问题,或者我们必须依赖CompletableFutures,我需要帮助才能找到答案 .

基本上,我们为单线程本身的服务器编写插件 . 这意味着,我们可以使用不同的钩子来添加逻辑,这个逻辑总是在主线程中运行,不能被阻塞 . 另外,在使用服务器的API时,我们必须在主线程内,因为给定的方法不是线程安全的 .

为了使用异步代码,我们've used the server'的调度程序产生一个 生产环境 者/消费者系统,它在后台运行异步任务并将结果同步回服务器's main thread. The implementation shouldn'这很重要,所以这里只是一个例子在实践中看起来像:

// execute hook that runs when a user on the server runs a command
override fun execute(sender: CommandSender, args: Array<out String>) {
    // call comes from the main thread
    db.fetchBalance(sender.name)
        // fetchBalance runs asynchronous code without blocking
        // the current thread by utilizing a consumer/producer system
        .thenAccept {
            // the CompletableFuture is resolved after completion

            // here we are in the main thread again, so that we can access
            // server methods in a thread safe manner
            sender.sendMessage("Your balance: $it")
        }
}

现在我的问题是,如果上面的例子可以用Kotlin代码替换它使它更具可读性,比如JavaScript中的async / await . 要记住,在JavaScript中我们可以这样做:

async function onBalanceRequest(client, name) {
  let balance = await db.fetchBalance(name);
  client.sendMessage("Your money: " + balance);
}

几天前我问了一个关于async / await的类似问题,这导致了一个看起来像这样的解决方案:

private fun onBalanceRequest(sender: CommandSender) {
    // call comes from the main thread
    GlobalScope.launch {
        // here we are within a new thread
        val money = db.fetchBalance(sender.name).join()
        // here we are within the same thread, which is
        // not the main thread, so the code below isn't safe
        sender.sendMessage("Your balance: $money")
    }
}

正如评论中所描述的那样,问题在于,在“等待未来”之后,代码在协程的线程中运行 . 所以我的问题是,如果我们能够实现像我用协同程序描述的东西,或者它们根本不是针对这个用例而制作的 . 我已经读过为生成的协程指定一个线程的可能性,但是这个线程会被阻塞,所以这不会起作用 .

如果CompletableFutures是解决这个问题的唯一方法,我们会坚持使用它们,但我想尝试协同程序,因为它们比CompletableFutures更好地编写和处理 .

谢谢

回答(1)

2 years ago

试试 withContext 功能 . 将代码包装在其中,它将在所需的上下文中执行 .

例如:

withContext(Dispatchers.Main) {
    //This will run in Main Thread
}

您可以使用您选择的CoroutinesContext替换 Dispatchers.Main

注意: withContext 函数是"suspending"函数,它必须仅在 Coroutine Scope 中执行