Kotlin协程-如何在后台运行并在调用线程中使用结果?

Kotlin协程-如何在后台运行并在调用线程中使用结果?,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,其主要思想是让非挂起函数运行background并使用callerthread(callback:(SomeModel)->Unit)在后台异步运行一些工作(另一个线程),工作完成后在调用线程(启动运行background并使用callerthread的线程)中运行callback 下面我写了一个示例代码,但我不确定它有多正确,是否可能。我用println(“1/2/3/…)标记了所需的调用顺序。 getDispatcherFromCurrentThread-如果可以实现此功能,则可以使用解决方

其主要思想是让非挂起函数
运行background并使用callerthread(callback:(SomeModel)->Unit)
在后台异步运行一些工作(另一个线程),工作完成后在调用线程(启动
运行background并使用callerthread的线程)中运行callback

下面我写了一个示例代码,但我不确定它有多正确,是否可能。我用
println(“1/2/3/…)
标记了所需的调用顺序。
getDispatcherFromCurrentThread
-如果可以实现此功能,则可以使用解决方案,但我不知道如何实现它,这样做是否正确

因此,请不要认为它是唯一的解决方案。
import kotlinx.coroutines.*
import kotlin.concurrent.thread

fun main() {
    println("1")
    runInBackgroundAndUseInCallerThread {
        println("4")
        println("Hello ${it.someField} from ${Thread.currentThread().name}") // should be "Hello TestField from main"
    }
    println("2")
    thread(name = "Second thread") {
        runInBackgroundAndUseInCallerThread {
            println("5")
            println("Hello ${it.someField} from ${Thread.currentThread().name}") // should be "Hello TestField from Second thread"
        }
    }
    println("3")
    Thread.sleep(3000)
    println("6")
}

fun runInBackgroundAndUseInCallerThread(callback: (SomeModel) -> Unit) {
    val dispatcherFromCallerThread: CoroutineDispatcher = getDispatcherFromCurrentThread()
    CoroutineScope(Dispatchers.IO).launch {
        val result: SomeModel = getModelResult()
        launch(dispatcherFromCallerThread) { callback(result) }
    }
}

data class SomeModel(val someField: String)

suspend fun getModelResult(): SomeModel {
    delay(1000)
    return SomeModel("TestField")
}

fun getDispatcherFromCurrentThread(): CoroutineDispatcher {
    // TODO: Create dispatcher from current thread... How to do that?
}

除非线程被设计为作为调度器工作,否则没有一种通用的方法使它这样做。 想到的唯一方法是,
runBlocking
是可重入的,它将在现有线程中创建一个事件循环,但是它将阻止所有非协程代码在该线程上执行,直到它完成

这最终看起来像:

fun runInBackgroundAndUseInCallerThread(callback: (SomeModel) -> Unit) {
    callback(runBlocking(Dispatchers.IO) {
        getModelResult()
    })
}

dispatcher
实际上是一个
coroutineContext
,当在
范围内使用时,它是有意义的
所以,若您想要将父范围的分派器传递给子范围,您可以这样做

GlobalScope.launch {
        val dispatcher = this.coroutineContext
        CoroutineScope(dispatcher).launch {

        }
}
因此,
getDispatcherFromCurrentThread
应该是这样的

fun getDispatcherFromCurrentThread(scope: CoroutineScope): CoroutineContext {
    return scope.coroutineContext
}

在后台(另一个线程)异步运行一些工作,并在工作完成后在调用线程中运行回调

首先尝试回答这个问题:当后台工作正在进行时,调用线程应该做什么

显然,它不能继续到代码的下一行,该行应该在完成后台工作后运行

您也不希望它阻塞并等待

那么它应该运行什么代码呢

唯一合理的答案如下:调用线程应该在其最高执行级别(入口点函数)运行无限事件循环。问题中的代码应该位于提交给事件循环的事件处理程序中。在您希望等待后台工作的时候,处理程序必须返回,以便线程可以继续处理其他事件,并且您必须在后台工作完成时准备好提交另一个处理程序。与您的
回调相对应的第二个处理程序称为continuation,Kotlin自动提供它。事实上,你不需要自己回电话

然而,现在最敏感的问题出现了:您将如何向事件循环提交延续?这不是您可以抽象的东西,您必须使用特定于所讨论的事件循环的一些API


这就是为什么Kotlin有了
调度器的概念。它捕获了将continuations分派到所需线程的具体情况。您似乎希望在不需要为每个特定事件循环编写专用的调度程序的情况下解决此问题,不幸的是,这是不可能的。

不幸的是,这将阻止调用线程(如您所说)这就是异步调用的主要思想。最后,在等待工作时需要阻塞线程,而无需建立契约。唯一可靠的方法是自己阻塞和处理已发送的代码(通过调用
runBlocking
),你的答案是有意义的。让我们假设我在GUI线程中运行
fun runinbackground并使用callerThread
,我想在后台完成工作,然后在GUI中运行回调,但我不想硬编码
Dispatchers.Main
。这“有趣”我还希望能够在后台工作的服务(非GUI)中运行,而不是阻止它,并在该服务的线程中运行回调。
在该服务的线程中运行回调
——这里我们回到我的回答中的问题。您需要一种向服务线程提交事件的方法。您通常无法解决这个问题,因为
线程
抽象没有任何与之关联的固有事件循环。因此,除非您能够
启动(serviceThreadDispatcher)
,然后以常规方式执行所有操作,否则无法实现您的目标。这不是本主题中描述的内容。
GlobalScope.launch {
            val dispatcher = getDispatcherFromCurrentThread(this)
            CoroutineScope(dispatcher).launch {

            }
    }