在主线程中运行无阻塞协程

时间:2018-11-07 10:55:52

标签: kotlin kotlinx.coroutines

我们有一个特殊的用例,我需要帮助找出是否可以解决Kotlin协程的问题,或者是否必须依赖CompletableFutures。

基本上,我们为单线程本身的服务器编写插件。这意味着,我们可以使用不同的钩子来添加逻辑,并且该逻辑始终在主线程中运行,并且必须禁止该主线程。此外,在使用服务器的API时,由于给定的方法不是线程安全的,因此我们必须位于主线程中。

为使此代码与异步代码一起使用,我们使用服务器的调度程序生成了一个生产者/消费者系统,该系统在后台运行异步任务,并将结果同步回到服务器的主线程。实现的重要性不那么重要,因此,这里仅是一个示例,说明实际情况:

// 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);
}

几天前,我曾问过一个关于异步/等待的类似问题,这导致了一个看起来像这样的解决方案:

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更好地编写和处理。

谢谢

2 个答案:

答案 0 :(得分:1)

尝试使用withContext函数。将您的代码包装在其中,它将在所需的上下文中执行。

例如:

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

您可以将Dispatchers.Main替换为您选择的CoroutinesContext

注意:withContext函数是“挂起”函数,必须仅在Coroutine Scope

中执行

答案 1 :(得分:0)

立即执行

如果确实需要立即执行怎么办?为此,您可以使用

Dispatchers.Main.immediate

在正确的上下文中立即执行协程

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    launch(Dispatchers.Main.immediate) {
        log("A")
    }

    log("B")
}

打印

OUTPUT:
// A
// B